Compare commits
162
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e90600c03 | ||
|
|
19410dfa3d | ||
|
|
78f94511ff | ||
|
|
d88d0bdd80 | ||
|
|
47f8b2cd0c | ||
|
|
ffd47081bd | ||
|
|
77db0d0701 | ||
|
|
e757237029 | ||
|
|
04ae38076f | ||
|
|
2ddf613c58 | ||
|
|
1bf3ad9a08 | ||
|
|
9086650fe2 | ||
|
|
8c813097db | ||
|
|
12403b75d6 | ||
|
|
480cf4952d | ||
|
|
d7e928df26 | ||
|
|
8957277e5d | ||
|
|
60b355b500 | ||
|
|
d2e324b5fd | ||
|
|
15638e9b8b | ||
|
|
ca2fa5ff06 | ||
|
|
888d3cb3fa | ||
|
|
8fcf476206 | ||
|
|
29f07e7e06 | ||
|
|
2cde42c7ec | ||
|
|
d12c38c344 | ||
|
|
5cc41c9ed5 | ||
|
|
c05a79cf61 | ||
|
|
f71f43e54a | ||
|
|
db87cde09e | ||
|
|
3d31d4dd7f | ||
|
|
42a457ff5d | ||
|
|
ade658ad86 | ||
|
|
bce2a07d03 | ||
|
|
dc757598f1 | ||
|
|
3b6ae772bf | ||
|
|
e8fe3c7c05 | ||
|
|
0477b8ca14 | ||
|
|
aed156f359 | ||
|
|
9013450e00 | ||
|
|
3b2b1b7b8c | ||
|
|
455a05481c | ||
|
|
626f487232 | ||
|
|
8fe3c91813 | ||
|
|
c4e111b554 | ||
|
|
12c5739287 | ||
|
|
78724f7459 | ||
|
|
1038d1500e | ||
|
|
7ae09ce1bf | ||
|
|
aef4b582db | ||
|
|
69ebdc63e3 | ||
|
|
2397f817fb | ||
|
|
95c83725d7 | ||
|
|
fa3bc05d3d | ||
|
|
18bfda35b3 | ||
|
|
5c9acfb4e9 | ||
|
|
e49ef773d8 | ||
|
|
e21d7552d3 | ||
|
|
6b7a707fef | ||
|
|
727fa67735 | ||
|
|
fc25f43448 | ||
|
|
631598b6b6 | ||
|
|
4e28481f27 | ||
|
|
16282f6a66 | ||
|
|
6956436a76 | ||
|
|
9a1c54a82d | ||
|
|
3e07320d22 | ||
|
|
1253264382 | ||
|
|
2ee911e33a | ||
|
|
cdcf02c943 | ||
|
|
3b7694b163 | ||
|
|
d1dd319091 | ||
|
|
9917cb4273 | ||
|
|
48452a42f4 | ||
|
|
6d4551bca2 | ||
|
|
e94b276f64 | ||
|
|
c51492c674 | ||
|
|
762a39330a | ||
|
|
44956176a1 | ||
|
|
1303469087 | ||
|
|
f78f278e2f | ||
|
|
b017d05ed9 | ||
|
|
bd97226c95 | ||
|
|
a8b930e351 | ||
|
|
e1e8857195 | ||
|
|
b5c6d90abd | ||
|
|
fd832d7593 | ||
|
|
29e44ac6d0 | ||
|
|
58ee2e125a | ||
|
|
89d0b2cb6e | ||
|
|
476077ab84 | ||
|
|
586552013e | ||
|
|
3dae3ff3ad | ||
|
|
29171565ed | ||
|
|
55fcbe2dde | ||
|
|
7f4b2d66d4 | ||
|
|
804a42c271 | ||
|
|
561fafa198 | ||
|
|
aaf70cf1cf | ||
|
|
0685e04aae | ||
|
|
b6bf6bd414 | ||
|
|
9f70b93ff3 | ||
|
|
505ede2761 | ||
|
|
56bf543f7e | ||
|
|
216de4d123 | ||
|
|
ef90b7a3d8 | ||
|
|
58dd2633ba | ||
|
|
c62c512ec4 | ||
|
|
e086cb0d86 | ||
|
|
b4d37fa16b | ||
|
|
a9d42e8c7e | ||
|
|
81060a72a4 | ||
|
|
881d4bf644 | ||
|
|
9581264286 | ||
|
|
7c5c88b17a | ||
|
|
351cc940ce | ||
|
|
9c2147aced | ||
|
|
cb8f6f1a39 | ||
|
|
a92639e391 | ||
|
|
dafedddc1a | ||
|
|
9f905ce2e6 | ||
|
|
dca14ef82d | ||
|
|
ac249c75c4 | ||
|
|
41e269e873 | ||
|
|
b7a83dcbcd | ||
|
|
fd993feece | ||
|
|
773be6f8ec | ||
|
|
77e463990a | ||
|
|
8f6ff4bc41 | ||
|
|
ab56374b2f | ||
|
|
fa305d76e8 | ||
|
|
1de3ff1a6d | ||
|
|
106319406b | ||
|
|
a5e9cd94e1 | ||
|
|
27ac262a26 | ||
|
|
ae4c8d818f | ||
|
|
c031cb19d2 | ||
|
|
ebae547a60 | ||
|
|
cc47ccaa2c | ||
|
|
d53097f77d | ||
|
|
9911c5dc12 | ||
|
|
19aef3e94b | ||
|
|
23230e0143 | ||
|
|
f92cea144c | ||
|
|
e6cf8c39b7 | ||
|
|
c5c0845151 | ||
|
|
5d9fb55370 | ||
|
|
721a2f5052 | ||
|
|
7320957405 | ||
|
|
e4d072c79c | ||
|
|
71ee10c889 | ||
|
|
978a8a4cb2 | ||
|
|
cd06b2b337 | ||
|
|
6eb06377cc | ||
|
|
94892e7168 | ||
|
|
fe744abe26 | ||
|
|
a902b49bc5 | ||
|
|
9c07b6dd46 | ||
|
|
3b7b06dbec | ||
|
|
597a4164e8 | ||
|
|
678c00f254 | ||
|
|
7298002da7 |
@@ -180,8 +180,6 @@ ANDROID_SHA256_CERT_FINGERPRINT=AC:73:8E:DE:EB:56:EA:CC:10:87:02:A7:65:37:7B:38:
|
|||||||
## Sentry
|
## Sentry
|
||||||
# SENTRY_DSN=
|
# SENTRY_DSN=
|
||||||
|
|
||||||
## LogRocket
|
|
||||||
# LOG_ROCKET_PROJECT_ID=xxxxx/some-project
|
|
||||||
|
|
||||||
# MICROSOFT CLARITY
|
# MICROSOFT CLARITY
|
||||||
# MS_CLARITY_TOKEN=xxxxxxxxx
|
# MS_CLARITY_TOKEN=xxxxxxxxx
|
||||||
@@ -251,9 +249,6 @@ AZURE_APP_SECRET=
|
|||||||
## OpenAI key
|
## OpenAI key
|
||||||
# OPENAI_API_KEY=
|
# OPENAI_API_KEY=
|
||||||
|
|
||||||
# Sentiment analysis model file path
|
|
||||||
SENTIMENT_FILE_PATH=
|
|
||||||
|
|
||||||
# Housekeeping/Performance related configurations
|
# Housekeeping/Performance related configurations
|
||||||
# Set to true if you want to remove stale contact inboxes
|
# Set to true if you want to remove stale contact inboxes
|
||||||
# contact_inboxes with no conversation older than 90 days will be removed
|
# contact_inboxes with no conversation older than 90 days will be removed
|
||||||
|
|||||||
@@ -1,7 +1,2 @@
|
|||||||
## All javascript files should be reviewed by pranav before merging
|
|
||||||
*.js @pranavrajs
|
|
||||||
*.vue @pranavrajs
|
|
||||||
|
|
||||||
|
|
||||||
## All enterprise related files should be reviewed by sojan before merging
|
## All enterprise related files should be reviewed by sojan before merging
|
||||||
/enterprise/* @sojan-official
|
/enterprise/* @sojan-official
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
## github action to check deployment success
|
||||||
|
## curl the deployment url and check for 200 status
|
||||||
|
## deployment url will be of the form chatwoot-pr-<pr_number>.herokuapp.com
|
||||||
|
name: Deploy Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deployment_check:
|
||||||
|
name: Check Deployment
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Install jq
|
||||||
|
run: sudo apt-get install -y jq
|
||||||
|
- name: Print Deployment URL
|
||||||
|
run: echo "https://chatwoot-pr-${{ github.event.pull_request.number }}.herokuapp.com"
|
||||||
|
- name: Check Deployment Status
|
||||||
|
run: |
|
||||||
|
max_attempts=10
|
||||||
|
attempt=1
|
||||||
|
status_code=0
|
||||||
|
echo "Waiting for review app to be deployed/redeployed, trying in 10 minutes..."
|
||||||
|
sleep 600
|
||||||
|
while [ $attempt -le $max_attempts ]; do
|
||||||
|
response=$(curl -s -o /dev/null -w "%{http_code}" https://chatwoot-pr-${{ github.event.pull_request.number }}.herokuapp.com/api)
|
||||||
|
status_code=$(echo $response | head -n 1)
|
||||||
|
if [ $status_code -eq 200 ]; then
|
||||||
|
body=$(curl -s https://chatwoot-pr-${{ github.event.pull_request.number }}.herokuapp.com/api)
|
||||||
|
if echo "$body" | jq -e '.version and .timestamp and .queue_services == "ok" and .data_services == "ok"' > /dev/null; then
|
||||||
|
echo "Deployment successful"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "Deployment status unknown, retrying in 3 minutes..."
|
||||||
|
sleep 180
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Waiting for review app to be ready, retrying in 3 minutes..."
|
||||||
|
sleep 180
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "Deployment failed after $max_attempts attempts"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -73,6 +73,7 @@ jobs:
|
|||||||
spec/enterprise/controllers/api/v1/accounts/response_sources_controller_spec.rb \
|
spec/enterprise/controllers/api/v1/accounts/response_sources_controller_spec.rb \
|
||||||
spec/enterprise/services/enterprise/message_templates/response_bot_service_spec.rb \
|
spec/enterprise/services/enterprise/message_templates/response_bot_service_spec.rb \
|
||||||
spec/enterprise/controllers/enterprise/api/v1/accounts/inboxes_controller_spec.rb:47 \
|
spec/enterprise/controllers/enterprise/api/v1/accounts/inboxes_controller_spec.rb:47 \
|
||||||
|
spec/enterprise/jobs/enterprise/account/conversations_resolution_scheduler_job_spec.rb \
|
||||||
--profile=10 \
|
--profile=10 \
|
||||||
--format documentation
|
--format documentation
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ require:
|
|||||||
- rubocop-performance
|
- rubocop-performance
|
||||||
- rubocop-rails
|
- rubocop-rails
|
||||||
- rubocop-rspec
|
- rubocop-rspec
|
||||||
|
- ./rubocop/use_from_email.rb
|
||||||
|
- ./rubocop/custom_cop_location.rb
|
||||||
|
|
||||||
Layout/LineLength:
|
Layout/LineLength:
|
||||||
Max: 150
|
Max: 150
|
||||||
@@ -140,6 +142,16 @@ RSpec/MultipleExpectations:
|
|||||||
RSpec/MultipleMemoizedHelpers:
|
RSpec/MultipleMemoizedHelpers:
|
||||||
Max: 14
|
Max: 14
|
||||||
|
|
||||||
|
# custom rules
|
||||||
|
UseFromEmail:
|
||||||
|
Enabled: true
|
||||||
|
Exclude:
|
||||||
|
- 'app/models/user.rb'
|
||||||
|
- 'app/models/contact.rb'
|
||||||
|
|
||||||
|
CustomCopLocation:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
NewCops: enable
|
NewCops: enable
|
||||||
Exclude:
|
Exclude:
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ source 'https://rubygems.org'
|
|||||||
ruby '3.2.2'
|
ruby '3.2.2'
|
||||||
|
|
||||||
##-- base gems for rails --##
|
##-- base gems for rails --##
|
||||||
gem 'rack-cors', require: 'rack/cors'
|
gem 'rack-cors', '2.0.0', require: 'rack/cors'
|
||||||
gem 'rails', '~> 7.0.8.0'
|
gem 'rails', '~> 7.0.8.1'
|
||||||
# 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
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ gem 'webpacker'
|
|||||||
gem 'barnes'
|
gem 'barnes'
|
||||||
|
|
||||||
##--- gems for authentication & authorization ---##
|
##--- gems for authentication & authorization ---##
|
||||||
gem 'devise', '>= 4.9.3'
|
gem 'devise', '>= 4.9.4'
|
||||||
gem 'devise-secure_password', git: 'https://github.com/chatwoot/devise-secure_password', branch: 'chatwoot'
|
gem 'devise-secure_password', git: 'https://github.com/chatwoot/devise-secure_password', branch: 'chatwoot'
|
||||||
gem 'devise_token_auth'
|
gem 'devise_token_auth'
|
||||||
# authorization
|
# authorization
|
||||||
@@ -165,7 +165,7 @@ gem 'audited', '~> 5.4', '>= 5.4.1'
|
|||||||
|
|
||||||
# need for google auth
|
# need for google auth
|
||||||
gem 'omniauth', '>= 2.1.2'
|
gem 'omniauth', '>= 2.1.2'
|
||||||
gem 'omniauth-google-oauth2'
|
gem 'omniauth-google-oauth2', '>= 1.1.2'
|
||||||
gem 'omniauth-rails_csrf_protection', '~> 1.0'
|
gem 'omniauth-rails_csrf_protection', '~> 1.0'
|
||||||
|
|
||||||
## Gems for reponse bot
|
## Gems for reponse bot
|
||||||
@@ -175,9 +175,6 @@ gem 'pgvector'
|
|||||||
# Convert Website HTML to Markdown
|
# Convert Website HTML to Markdown
|
||||||
gem 'reverse_markdown'
|
gem 'reverse_markdown'
|
||||||
|
|
||||||
# Sentiment analysis
|
|
||||||
gem 'informers'
|
|
||||||
|
|
||||||
### Gems required only in specific deployment environments ###
|
### Gems required only in specific deployment environments ###
|
||||||
##############################################################
|
##############################################################
|
||||||
|
|
||||||
@@ -203,7 +200,7 @@ group :development do
|
|||||||
gem 'rack-mini-profiler', '>= 3.2.0', require: false
|
gem 'rack-mini-profiler', '>= 3.2.0', require: false
|
||||||
gem 'stackprof'
|
gem 'stackprof'
|
||||||
# Should install the associated chrome extension to view query logs
|
# Should install the associated chrome extension to view query logs
|
||||||
gem 'meta_request'
|
gem 'meta_request', '>= 0.8.0'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
|
|||||||
+103
-110
@@ -33,70 +33,70 @@ GIT
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (7.0.8)
|
actioncable (7.0.8.1)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.0.8.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.0.8.1)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailbox (7.0.8)
|
actionmailbox (7.0.8.1)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.0.8.1)
|
||||||
activejob (= 7.0.8)
|
activejob (= 7.0.8.1)
|
||||||
activerecord (= 7.0.8)
|
activerecord (= 7.0.8.1)
|
||||||
activestorage (= 7.0.8)
|
activestorage (= 7.0.8.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.0.8.1)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
actionmailer (7.0.8)
|
actionmailer (7.0.8.1)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.0.8.1)
|
||||||
actionview (= 7.0.8)
|
actionview (= 7.0.8.1)
|
||||||
activejob (= 7.0.8)
|
activejob (= 7.0.8.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.0.8.1)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (7.0.8)
|
actionpack (7.0.8.1)
|
||||||
actionview (= 7.0.8)
|
actionview (= 7.0.8.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.0.8.1)
|
||||||
rack (~> 2.0, >= 2.2.4)
|
rack (~> 2.0, >= 2.2.4)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||||
actiontext (7.0.8)
|
actiontext (7.0.8.1)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.0.8.1)
|
||||||
activerecord (= 7.0.8)
|
activerecord (= 7.0.8.1)
|
||||||
activestorage (= 7.0.8)
|
activestorage (= 7.0.8.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.0.8.1)
|
||||||
globalid (>= 0.6.0)
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (7.0.8)
|
actionview (7.0.8.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.0.8.1)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||||
active_record_query_trace (1.8)
|
active_record_query_trace (1.8)
|
||||||
activejob (7.0.8)
|
activejob (7.0.8.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.0.8.1)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (7.0.8)
|
activemodel (7.0.8.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.0.8.1)
|
||||||
activerecord (7.0.8)
|
activerecord (7.0.8.1)
|
||||||
activemodel (= 7.0.8)
|
activemodel (= 7.0.8.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.0.8.1)
|
||||||
activerecord-import (1.4.1)
|
activerecord-import (1.4.1)
|
||||||
activerecord (>= 4.2)
|
activerecord (>= 4.2)
|
||||||
activestorage (7.0.8)
|
activestorage (7.0.8.1)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.0.8.1)
|
||||||
activejob (= 7.0.8)
|
activejob (= 7.0.8.1)
|
||||||
activerecord (= 7.0.8)
|
activerecord (= 7.0.8.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.0.8.1)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
mini_mime (>= 1.1.0)
|
mini_mime (>= 1.1.0)
|
||||||
activesupport (7.0.8)
|
activesupport (7.0.8.1)
|
||||||
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)
|
||||||
@@ -149,9 +149,8 @@ GEM
|
|||||||
multi_json (~> 1)
|
multi_json (~> 1)
|
||||||
statsd-ruby (~> 1.1)
|
statsd-ruby (~> 1.1)
|
||||||
base64 (0.1.1)
|
base64 (0.1.1)
|
||||||
bcrypt (3.1.19)
|
bcrypt (3.1.20)
|
||||||
bindex (0.8.1)
|
bindex (0.8.1)
|
||||||
blingfire (0.1.8)
|
|
||||||
bootsnap (1.16.0)
|
bootsnap (1.16.0)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
brakeman (5.4.1)
|
brakeman (5.4.1)
|
||||||
@@ -194,7 +193,7 @@ GEM
|
|||||||
irb (>= 1.5.0)
|
irb (>= 1.5.0)
|
||||||
reline (>= 0.3.1)
|
reline (>= 0.3.1)
|
||||||
declarative (0.0.20)
|
declarative (0.0.20)
|
||||||
devise (4.9.3)
|
devise (4.9.4)
|
||||||
bcrypt (~> 3.0)
|
bcrypt (~> 3.0)
|
||||||
orm_adapter (~> 0.1)
|
orm_adapter (~> 0.1)
|
||||||
railties (>= 4.1.0)
|
railties (>= 4.1.0)
|
||||||
@@ -237,9 +236,8 @@ GEM
|
|||||||
railties (>= 5.0.0)
|
railties (>= 5.0.0)
|
||||||
faker (3.2.0)
|
faker (3.2.0)
|
||||||
i18n (>= 1.8.11, < 2)
|
i18n (>= 1.8.11, < 2)
|
||||||
faraday (2.7.4)
|
faraday (2.9.0)
|
||||||
faraday-net_http (>= 2.0, < 3.1)
|
faraday-net_http (>= 2.0, < 3.2)
|
||||||
ruby2_keywords (>= 0.0.4)
|
|
||||||
faraday-follow_redirects (0.3.0)
|
faraday-follow_redirects (0.3.0)
|
||||||
faraday (>= 1, < 3)
|
faraday (>= 1, < 3)
|
||||||
faraday-mashify (0.1.1)
|
faraday-mashify (0.1.1)
|
||||||
@@ -247,7 +245,8 @@ GEM
|
|||||||
hashie
|
hashie
|
||||||
faraday-multipart (1.0.4)
|
faraday-multipart (1.0.4)
|
||||||
multipart-post (~> 2)
|
multipart-post (~> 2)
|
||||||
faraday-net_http (3.0.2)
|
faraday-net_http (3.1.0)
|
||||||
|
net-http
|
||||||
faraday-net_http_persistent (2.1.0)
|
faraday-net_http_persistent (2.1.0)
|
||||||
faraday (~> 2.5)
|
faraday (~> 2.5)
|
||||||
net-http-persistent (~> 4.0)
|
net-http-persistent (~> 4.0)
|
||||||
@@ -316,15 +315,15 @@ GEM
|
|||||||
google-cloud-translate-v3 (0.6.0)
|
google-cloud-translate-v3 (0.6.0)
|
||||||
gapic-common (>= 0.17.1, < 2.a)
|
gapic-common (>= 0.17.1, < 2.a)
|
||||||
google-cloud-errors (~> 1.0)
|
google-cloud-errors (~> 1.0)
|
||||||
google-protobuf (3.25.2)
|
google-protobuf (3.25.3)
|
||||||
google-protobuf (3.25.2-arm64-darwin)
|
google-protobuf (3.25.3-arm64-darwin)
|
||||||
google-protobuf (3.25.2-x86_64-darwin)
|
google-protobuf (3.25.3-x86_64-darwin)
|
||||||
google-protobuf (3.25.2-x86_64-linux)
|
google-protobuf (3.25.3-x86_64-linux)
|
||||||
googleapis-common-protos (1.4.0)
|
googleapis-common-protos (1.4.0)
|
||||||
google-protobuf (~> 3.14)
|
google-protobuf (~> 3.14)
|
||||||
googleapis-common-protos-types (~> 1.2)
|
googleapis-common-protos-types (~> 1.2)
|
||||||
grpc (~> 1.27)
|
grpc (~> 1.27)
|
||||||
googleapis-common-protos-types (1.11.0)
|
googleapis-common-protos-types (1.14.0)
|
||||||
google-protobuf (~> 3.18)
|
google-protobuf (~> 3.18)
|
||||||
googleauth (1.5.2)
|
googleauth (1.5.2)
|
||||||
faraday (>= 0.17.3, < 3.a)
|
faraday (>= 0.17.3, < 3.a)
|
||||||
@@ -335,14 +334,17 @@ GEM
|
|||||||
signet (>= 0.16, < 2.a)
|
signet (>= 0.16, < 2.a)
|
||||||
groupdate (6.2.1)
|
groupdate (6.2.1)
|
||||||
activesupport (>= 5.2)
|
activesupport (>= 5.2)
|
||||||
grpc (1.54.3)
|
grpc (1.62.0)
|
||||||
google-protobuf (~> 3.21)
|
google-protobuf (~> 3.25)
|
||||||
googleapis-common-protos-types (~> 1.0)
|
googleapis-common-protos-types (~> 1.0)
|
||||||
grpc (1.54.3-x86_64-darwin)
|
grpc (1.62.0-arm64-darwin)
|
||||||
google-protobuf (~> 3.21)
|
google-protobuf (~> 3.25)
|
||||||
googleapis-common-protos-types (~> 1.0)
|
googleapis-common-protos-types (~> 1.0)
|
||||||
grpc (1.54.3-x86_64-linux)
|
grpc (1.62.0-x86_64-darwin)
|
||||||
google-protobuf (~> 3.21)
|
google-protobuf (~> 3.25)
|
||||||
|
googleapis-common-protos-types (~> 1.0)
|
||||||
|
grpc (1.62.0-x86_64-linux)
|
||||||
|
google-protobuf (~> 3.25)
|
||||||
googleapis-common-protos-types (~> 1.0)
|
googleapis-common-protos-types (~> 1.0)
|
||||||
haikunator (1.1.1)
|
haikunator (1.1.1)
|
||||||
hairtrigger (1.0.0)
|
hairtrigger (1.0.0)
|
||||||
@@ -366,15 +368,11 @@ 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.14.1)
|
i18n (1.14.4)
|
||||||
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)
|
||||||
ruby-vips (>= 2.0.17, < 3)
|
ruby-vips (>= 2.0.17, < 3)
|
||||||
informers (0.2.0)
|
|
||||||
blingfire (>= 0.1.7)
|
|
||||||
numo-narray
|
|
||||||
onnxruntime (>= 0.5.1)
|
|
||||||
io-console (0.6.0)
|
io-console (0.6.0)
|
||||||
irb (1.7.2)
|
irb (1.7.2)
|
||||||
reline (>= 0.3.6)
|
reline (>= 0.3.6)
|
||||||
@@ -394,7 +392,8 @@ GEM
|
|||||||
hana (~> 1.3)
|
hana (~> 1.3)
|
||||||
regexp_parser (~> 2.0)
|
regexp_parser (~> 2.0)
|
||||||
uri_template (~> 0.7)
|
uri_template (~> 0.7)
|
||||||
jwt (2.7.0)
|
jwt (2.8.1)
|
||||||
|
base64
|
||||||
kaminari (1.2.2)
|
kaminari (1.2.2)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
kaminari-actionview (= 1.2.2)
|
kaminari-actionview (= 1.2.2)
|
||||||
@@ -451,17 +450,17 @@ GEM
|
|||||||
marcel (1.0.2)
|
marcel (1.0.2)
|
||||||
maxminddb (0.1.22)
|
maxminddb (0.1.22)
|
||||||
memoist (0.16.2)
|
memoist (0.16.2)
|
||||||
meta_request (0.7.4)
|
meta_request (0.8.2)
|
||||||
rack-contrib (>= 1.1, < 3)
|
rack-contrib (>= 1.1, < 3)
|
||||||
railties (>= 3.0.0, < 7.1)
|
railties (>= 3.0.0, < 8)
|
||||||
method_source (1.0.0)
|
method_source (1.0.0)
|
||||||
mime-types (3.4.1)
|
mime-types (3.4.1)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2023.0218.1)
|
mime-types-data (3.2023.0218.1)
|
||||||
mini_magick (4.12.0)
|
mini_magick (4.12.0)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.5)
|
mini_portile2 (2.8.6)
|
||||||
minitest (5.21.2)
|
minitest (5.22.3)
|
||||||
mock_redis (0.36.0)
|
mock_redis (0.36.0)
|
||||||
ruby2_keywords
|
ruby2_keywords
|
||||||
msgpack (1.7.0)
|
msgpack (1.7.0)
|
||||||
@@ -470,6 +469,8 @@ GEM
|
|||||||
multipart-post (2.3.0)
|
multipart-post (2.3.0)
|
||||||
neighbor (0.2.3)
|
neighbor (0.2.3)
|
||||||
activerecord (>= 5.2)
|
activerecord (>= 5.2)
|
||||||
|
net-http (0.4.1)
|
||||||
|
uri
|
||||||
net-http-persistent (4.0.2)
|
net-http-persistent (4.0.2)
|
||||||
connection_pool (~> 2.2)
|
connection_pool (~> 2.2)
|
||||||
net-imap (0.4.9)
|
net-imap (0.4.9)
|
||||||
@@ -488,16 +489,15 @@ GEM
|
|||||||
newrelic_rpm (9.6.0)
|
newrelic_rpm (9.6.0)
|
||||||
base64
|
base64
|
||||||
nio4r (2.7.0)
|
nio4r (2.7.0)
|
||||||
nokogiri (1.16.2)
|
nokogiri (1.16.4)
|
||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.16.2-arm64-darwin)
|
nokogiri (1.16.4-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.16.2-x86_64-darwin)
|
nokogiri (1.16.4-x86_64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.16.2-x86_64-linux)
|
nokogiri (1.16.4-x86_64-linux)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
numo-narray (0.9.2.1)
|
|
||||||
oauth (1.1.0)
|
oauth (1.1.0)
|
||||||
oauth-tty (~> 1.0, >= 1.0.1)
|
oauth-tty (~> 1.0, >= 1.0.1)
|
||||||
snaky_hash (~> 2.0)
|
snaky_hash (~> 2.0)
|
||||||
@@ -515,25 +515,17 @@ GEM
|
|||||||
hashie (>= 3.4.6)
|
hashie (>= 3.4.6)
|
||||||
rack (>= 2.2.3)
|
rack (>= 2.2.3)
|
||||||
rack-protection
|
rack-protection
|
||||||
omniauth-google-oauth2 (1.1.1)
|
omniauth-google-oauth2 (1.1.2)
|
||||||
jwt (>= 2.0)
|
jwt (>= 2.0)
|
||||||
oauth2 (~> 2.0.6)
|
oauth2 (~> 2.0)
|
||||||
omniauth (~> 2.0)
|
omniauth (~> 2.0)
|
||||||
omniauth-oauth2 (~> 1.8.0)
|
omniauth-oauth2 (~> 1.8)
|
||||||
omniauth-oauth2 (1.8.0)
|
omniauth-oauth2 (1.8.0)
|
||||||
oauth2 (>= 1.4, < 3)
|
oauth2 (>= 1.4, < 3)
|
||||||
omniauth (~> 2.0)
|
omniauth (~> 2.0)
|
||||||
omniauth-rails_csrf_protection (1.0.1)
|
omniauth-rails_csrf_protection (1.0.1)
|
||||||
actionpack (>= 4.2)
|
actionpack (>= 4.2)
|
||||||
omniauth (~> 2.0)
|
omniauth (~> 2.0)
|
||||||
onnxruntime (0.7.6)
|
|
||||||
ffi
|
|
||||||
onnxruntime (0.7.6-arm64-darwin)
|
|
||||||
ffi
|
|
||||||
onnxruntime (0.7.6-x86_64-darwin)
|
|
||||||
ffi
|
|
||||||
onnxruntime (0.7.6-x86_64-linux)
|
|
||||||
ffi
|
|
||||||
openssl (3.1.0)
|
openssl (3.1.0)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
os (1.1.4)
|
os (1.1.4)
|
||||||
@@ -559,36 +551,37 @@ GEM
|
|||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
raabro (1.4.0)
|
raabro (1.4.0)
|
||||||
racc (1.7.3)
|
racc (1.7.3)
|
||||||
rack (2.2.8)
|
rack (2.2.9)
|
||||||
rack-attack (6.7.0)
|
rack-attack (6.7.0)
|
||||||
rack (>= 1.0, < 4)
|
rack (>= 1.0, < 4)
|
||||||
rack-contrib (2.4.0)
|
rack-contrib (2.4.0)
|
||||||
rack (< 4)
|
rack (< 4)
|
||||||
rack-cors (2.0.1)
|
rack-cors (2.0.0)
|
||||||
rack (>= 2.0.0)
|
rack (>= 2.0.0)
|
||||||
rack-mini-profiler (3.2.0)
|
rack-mini-profiler (3.2.0)
|
||||||
rack (>= 1.2.0)
|
rack (>= 1.2.0)
|
||||||
rack-protection (3.1.0)
|
rack-protection (3.2.0)
|
||||||
|
base64 (>= 0.1.0)
|
||||||
rack (~> 2.2, >= 2.2.4)
|
rack (~> 2.2, >= 2.2.4)
|
||||||
rack-proxy (0.7.6)
|
rack-proxy (0.7.6)
|
||||||
rack
|
rack
|
||||||
rack-test (2.1.0)
|
rack-test (2.1.0)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rack-timeout (0.6.3)
|
rack-timeout (0.6.3)
|
||||||
rails (7.0.8)
|
rails (7.0.8.1)
|
||||||
actioncable (= 7.0.8)
|
actioncable (= 7.0.8.1)
|
||||||
actionmailbox (= 7.0.8)
|
actionmailbox (= 7.0.8.1)
|
||||||
actionmailer (= 7.0.8)
|
actionmailer (= 7.0.8.1)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.0.8.1)
|
||||||
actiontext (= 7.0.8)
|
actiontext (= 7.0.8.1)
|
||||||
actionview (= 7.0.8)
|
actionview (= 7.0.8.1)
|
||||||
activejob (= 7.0.8)
|
activejob (= 7.0.8.1)
|
||||||
activemodel (= 7.0.8)
|
activemodel (= 7.0.8.1)
|
||||||
activerecord (= 7.0.8)
|
activerecord (= 7.0.8.1)
|
||||||
activestorage (= 7.0.8)
|
activestorage (= 7.0.8.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.0.8.1)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 7.0.8)
|
railties (= 7.0.8.1)
|
||||||
rails-dom-testing (2.2.0)
|
rails-dom-testing (2.2.0)
|
||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
minitest
|
minitest
|
||||||
@@ -596,15 +589,15 @@ GEM
|
|||||||
rails-html-sanitizer (1.6.0)
|
rails-html-sanitizer (1.6.0)
|
||||||
loofah (~> 2.21)
|
loofah (~> 2.21)
|
||||||
nokogiri (~> 1.14)
|
nokogiri (~> 1.14)
|
||||||
railties (7.0.8)
|
railties (7.0.8.1)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.0.8.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.0.8.1)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
thor (~> 1.0)
|
thor (~> 1.0)
|
||||||
zeitwerk (~> 2.5)
|
zeitwerk (~> 2.5)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
rake (13.1.0)
|
rake (13.2.1)
|
||||||
rb-fsevent (0.11.2)
|
rb-fsevent (0.11.2)
|
||||||
rb-inotify (0.10.1)
|
rb-inotify (0.10.1)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
@@ -765,7 +758,7 @@ GEM
|
|||||||
stripe (8.5.0)
|
stripe (8.5.0)
|
||||||
telephone_number (1.4.20)
|
telephone_number (1.4.20)
|
||||||
test-prof (1.2.1)
|
test-prof (1.2.1)
|
||||||
thor (1.3.0)
|
thor (1.3.1)
|
||||||
tilt (2.3.0)
|
tilt (2.3.0)
|
||||||
time_diff (0.3.0)
|
time_diff (0.3.0)
|
||||||
activesupport
|
activesupport
|
||||||
@@ -790,11 +783,12 @@ GEM
|
|||||||
unf_ext (0.0.8.2)
|
unf_ext (0.0.8.2)
|
||||||
unicode-display_width (2.4.2)
|
unicode-display_width (2.4.2)
|
||||||
uniform_notifier (1.16.0)
|
uniform_notifier (1.16.0)
|
||||||
|
uri (0.13.0)
|
||||||
uri_template (0.7.0)
|
uri_template (0.7.0)
|
||||||
valid_email2 (4.0.6)
|
valid_email2 (4.0.6)
|
||||||
activemodel (>= 3.2)
|
activemodel (>= 3.2)
|
||||||
mail (~> 2.5)
|
mail (~> 2.5)
|
||||||
version_gem (1.1.3)
|
version_gem (1.1.4)
|
||||||
warden (1.2.9)
|
warden (1.2.9)
|
||||||
rack (>= 2.0.9)
|
rack (>= 2.0.9)
|
||||||
web-console (4.2.1)
|
web-console (4.2.1)
|
||||||
@@ -823,7 +817,7 @@ GEM
|
|||||||
working_hours (1.4.1)
|
working_hours (1.4.1)
|
||||||
activesupport (>= 3.2)
|
activesupport (>= 3.2)
|
||||||
tzinfo
|
tzinfo
|
||||||
zeitwerk (2.6.12)
|
zeitwerk (2.6.13)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
arm64-darwin-20
|
arm64-darwin-20
|
||||||
@@ -862,7 +856,7 @@ DEPENDENCIES
|
|||||||
database_cleaner
|
database_cleaner
|
||||||
ddtrace
|
ddtrace
|
||||||
debug (~> 1.8)
|
debug (~> 1.8)
|
||||||
devise (>= 4.9.3)
|
devise (>= 4.9.4)
|
||||||
devise-secure_password!
|
devise-secure_password!
|
||||||
devise_token_auth
|
devise_token_auth
|
||||||
dotenv-rails
|
dotenv-rails
|
||||||
@@ -887,7 +881,6 @@ DEPENDENCIES
|
|||||||
hashie
|
hashie
|
||||||
html2text!
|
html2text!
|
||||||
image_processing
|
image_processing
|
||||||
informers
|
|
||||||
jbuilder
|
jbuilder
|
||||||
json_refs
|
json_refs
|
||||||
json_schemer
|
json_schemer
|
||||||
@@ -900,14 +893,14 @@ DEPENDENCIES
|
|||||||
listen
|
listen
|
||||||
lograge (~> 0.14.0)
|
lograge (~> 0.14.0)
|
||||||
maxminddb
|
maxminddb
|
||||||
meta_request
|
meta_request (>= 0.8.0)
|
||||||
mock_redis
|
mock_redis
|
||||||
neighbor
|
neighbor
|
||||||
net-smtp (~> 0.3.4)
|
net-smtp (~> 0.3.4)
|
||||||
newrelic-sidekiq-metrics (>= 1.6.2)
|
newrelic-sidekiq-metrics (>= 1.6.2)
|
||||||
newrelic_rpm
|
newrelic_rpm
|
||||||
omniauth (>= 2.1.2)
|
omniauth (>= 2.1.2)
|
||||||
omniauth-google-oauth2
|
omniauth-google-oauth2 (>= 1.1.2)
|
||||||
omniauth-oauth2
|
omniauth-oauth2
|
||||||
omniauth-rails_csrf_protection (~> 1.0)
|
omniauth-rails_csrf_protection (~> 1.0)
|
||||||
pg
|
pg
|
||||||
@@ -918,10 +911,10 @@ DEPENDENCIES
|
|||||||
puma
|
puma
|
||||||
pundit
|
pundit
|
||||||
rack-attack (>= 6.7.0)
|
rack-attack (>= 6.7.0)
|
||||||
rack-cors
|
rack-cors (= 2.0.0)
|
||||||
rack-mini-profiler (>= 3.2.0)
|
rack-mini-profiler (>= 3.2.0)
|
||||||
rack-timeout
|
rack-timeout
|
||||||
rails (~> 7.0.8.0)
|
rails (~> 7.0.8.1)
|
||||||
redis
|
redis
|
||||||
redis-namespace
|
redis-namespace
|
||||||
responders (>= 3.1.1)
|
responders (>= 3.1.1)
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ db_migrate:
|
|||||||
db_seed:
|
db_seed:
|
||||||
RAILS_ENV=$(RAILS_ENV) bundle exec rails db:seed
|
RAILS_ENV=$(RAILS_ENV) bundle exec rails db:seed
|
||||||
|
|
||||||
|
db_reset:
|
||||||
|
RAILS_ENV=$(RAILS_ENV) bundle exec rails db:reset
|
||||||
|
|
||||||
db:
|
db:
|
||||||
RAILS_ENV=$(RAILS_ENV) bundle exec rails db:chatwoot_prepare
|
RAILS_ENV=$(RAILS_ENV) bundle exec rails db:chatwoot_prepare
|
||||||
|
|
||||||
@@ -49,4 +52,4 @@ debug_worker:
|
|||||||
docker:
|
docker:
|
||||||
docker build -t $(APP_NAME) -f ./docker/Dockerfile .
|
docker build -t $(APP_NAME) -f ./docker/Dockerfile .
|
||||||
|
|
||||||
.PHONY: setup db_create db_migrate db_seed db console server burn docker run force_run debug debug_worker
|
.PHONY: setup db_create db_migrate db_seed db_reset db console server burn docker run force_run debug debug_worker
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ Customer engagement suite, an open-source alternative to Intercom, Zendesk, Sale
|
|||||||
<img src="https://img.shields.io/github/commit-activity/m/chatwoot/chatwoot" alt="Commits-per-month">
|
<img src="https://img.shields.io/github/commit-activity/m/chatwoot/chatwoot" alt="Commits-per-month">
|
||||||
<a title="Crowdin" target="_self" href="https://chatwoot.crowdin.com/chatwoot"><img src="https://badges.crowdin.net/e/37ced7eba411064bd792feb3b7a28b16/localized.svg"></a>
|
<a title="Crowdin" target="_self" href="https://chatwoot.crowdin.com/chatwoot"><img src="https://badges.crowdin.net/e/37ced7eba411064bd792feb3b7a28b16/localized.svg"></a>
|
||||||
<a href="https://discord.gg/cJXdrwS"><img src="https://img.shields.io/discord/647412545203994635" alt="Discord"></a>
|
<a href="https://discord.gg/cJXdrwS"><img src="https://img.shields.io/discord/647412545203994635" alt="Discord"></a>
|
||||||
<a href="https://huntr.dev/bounties/disclose"><img src="https://cdn.huntr.dev/huntr_security_badge_mono.svg" alt="Huntr"></a>
|
|
||||||
<a href="https://status.chatwoot.com"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fchatwoot%2Fstatus%2Fmaster%2Fapi%2Fchatwoot%2Fuptime.json" alt="uptime"></a>
|
<a href="https://status.chatwoot.com"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fchatwoot%2Fstatus%2Fmaster%2Fapi%2Fchatwoot%2Fuptime.json" alt="uptime"></a>
|
||||||
<a href="https://status.chatwoot.com"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fchatwoot%2Fstatus%2Fmaster%2Fapi%2Fchatwoot%2Fresponse-time.json" alt="response time"></a>
|
<a href="https://status.chatwoot.com"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fchatwoot%2Fstatus%2Fmaster%2Fapi%2Fchatwoot%2Fresponse-time.json" alt="response time"></a>
|
||||||
<a href="https://artifacthub.io/packages/helm/chatwoot/chatwoot"><img src="https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/artifact-hub" alt="Artifact HUB"></a>
|
<a href="https://artifacthub.io/packages/helm/chatwoot/chatwoot"><img src="https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/artifact-hub" alt="Artifact HUB"></a>
|
||||||
|
|||||||
@@ -72,6 +72,11 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "bundle exec rake test"
|
"test": "bundle exec rake test"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"review": {
|
||||||
|
"scripts": {
|
||||||
|
"postdeploy": "bundle exec rails db:seed"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class ContactIdentifyAction
|
|||||||
def existing_email_contact
|
def existing_email_contact
|
||||||
return if params[:email].blank?
|
return if params[:email].blank?
|
||||||
|
|
||||||
@existing_email_contact ||= account.contacts.find_by(email: params[:email])
|
@existing_email_contact ||= account.contacts.from_email(params[:email])
|
||||||
end
|
end
|
||||||
|
|
||||||
def existing_phone_number_contact
|
def existing_phone_number_contact
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ class AgentBuilder
|
|||||||
def perform
|
def perform
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
@user = find_or_create_user
|
@user = find_or_create_user
|
||||||
send_confirmation_if_required
|
|
||||||
create_account_user
|
create_account_user
|
||||||
end
|
end
|
||||||
@user
|
@user
|
||||||
@@ -27,18 +26,13 @@ class AgentBuilder
|
|||||||
# Finds a user by email or creates a new one with a temporary password.
|
# Finds a user by email or creates a new one with a temporary password.
|
||||||
# @return [User] the found or created user.
|
# @return [User] the found or created user.
|
||||||
def find_or_create_user
|
def find_or_create_user
|
||||||
user = User.find_by(email: email)
|
user = User.from_email(email)
|
||||||
return user if user
|
return user if user
|
||||||
|
|
||||||
temp_password = "1!aA#{SecureRandom.alphanumeric(12)}"
|
temp_password = "1!aA#{SecureRandom.alphanumeric(12)}"
|
||||||
User.create!(email: email, name: name, password: temp_password, password_confirmation: temp_password)
|
User.create!(email: email, name: name, password: temp_password, password_confirmation: temp_password)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sends confirmation instructions if the user is persisted and not confirmed.
|
|
||||||
def send_confirmation_if_required
|
|
||||||
@user.send_confirmation_instructions if user_needs_confirmation?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Checks if the user needs confirmation.
|
# Checks if the user needs confirmation.
|
||||||
# @return [Boolean] true if the user is persisted and not confirmed, false otherwise.
|
# @return [Boolean] true if the user is persisted and not confirmed, false otherwise.
|
||||||
def user_needs_confirmation?
|
def user_needs_confirmation?
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class ContactInboxWithContactBuilder
|
|||||||
def find_contact_by_email(email)
|
def find_contact_by_email(email)
|
||||||
return if email.blank?
|
return if email.blank?
|
||||||
|
|
||||||
account.contacts.find_by(email: email.downcase)
|
account.contacts.from_email(email)
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_contact_by_phone_number(phone_number)
|
def find_contact_by_phone_number(phone_number)
|
||||||
|
|||||||
@@ -53,7 +53,23 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||||||
end
|
end
|
||||||
|
|
||||||
def conversation
|
def conversation
|
||||||
@conversation ||= Conversation.find_by(conversation_params) || build_conversation
|
@conversation ||= set_conversation_based_on_inbox_config
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_conversation_based_on_inbox_config
|
||||||
|
if @inbox.lock_to_single_conversation
|
||||||
|
Conversation.where(conversation_params).order(created_at: :desc).first || build_conversation
|
||||||
|
else
|
||||||
|
find_or_build_for_multiple_conversations
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_or_build_for_multiple_conversations
|
||||||
|
# If lock to single conversation is disabled, we will create a new conversation if previous conversation is resolved
|
||||||
|
last_conversation = Conversation.where(conversation_params).where.not(status: :resolved).order(created_at: :desc).first
|
||||||
|
return build_conversation if last_conversation.nil?
|
||||||
|
|
||||||
|
last_conversation
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_conversation
|
def build_conversation
|
||||||
|
|||||||
@@ -69,9 +69,28 @@ class Messages::Instagram::MessageBuilder < Messages::Messenger::MessageBuilder
|
|||||||
end
|
end
|
||||||
|
|
||||||
def conversation
|
def conversation
|
||||||
@conversation ||= Conversation.where(conversation_params).find_by(
|
@conversation ||= set_conversation_based_on_inbox_config
|
||||||
"additional_attributes ->> 'type' = 'instagram_direct_message'"
|
end
|
||||||
) || build_conversation
|
|
||||||
|
def instagram_direct_message_conversation
|
||||||
|
Conversation.where(conversation_params)
|
||||||
|
.where("additional_attributes ->> 'type' = 'instagram_direct_message'")
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_conversation_based_on_inbox_config
|
||||||
|
if @inbox.lock_to_single_conversation
|
||||||
|
instagram_direct_message_conversation.order(created_at: :desc).first || build_conversation
|
||||||
|
else
|
||||||
|
find_or_build_for_multiple_conversations
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_or_build_for_multiple_conversations
|
||||||
|
last_conversation = instagram_direct_message_conversation.where.not(status: :resolved).order(created_at: :desc).first
|
||||||
|
|
||||||
|
return build_conversation if last_conversation.nil?
|
||||||
|
|
||||||
|
last_conversation
|
||||||
end
|
end
|
||||||
|
|
||||||
def message_content
|
def message_content
|
||||||
|
|||||||
@@ -54,6 +54,13 @@ class V2::ReportBuilder
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def bot_summary
|
||||||
|
{
|
||||||
|
bot_resolutions_count: bot_resolutions.count,
|
||||||
|
bot_handoffs_count: bot_handoffs.count
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def conversation_metrics
|
def conversation_metrics
|
||||||
if params[:type].equal?(:account)
|
if params[:type].equal?(:account)
|
||||||
live_conversations
|
live_conversations
|
||||||
@@ -71,6 +78,8 @@ class V2::ReportBuilder
|
|||||||
avg_first_response_time
|
avg_first_response_time
|
||||||
avg_resolution_time reply_time
|
avg_resolution_time reply_time
|
||||||
resolutions_count
|
resolutions_count
|
||||||
|
bot_resolutions_count
|
||||||
|
bot_handoffs_count
|
||||||
reply_time].include?(params[:metric])
|
reply_time].include?(params[:metric])
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -123,6 +132,7 @@ class V2::ReportBuilder
|
|||||||
unattended: @open_conversations.unattended.count
|
unattended: @open_conversations.unattended.count
|
||||||
}
|
}
|
||||||
metric[:unassigned] = @open_conversations.unassigned.count if params[:type].equal?(:account)
|
metric[:unassigned] = @open_conversations.unassigned.count if params[:type].equal?(:account)
|
||||||
|
metric[:pending] = @open_conversations.pending.count if params[:type].equal?(:account)
|
||||||
metric
|
metric
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
class V2::Reports::BotMetricsBuilder
|
||||||
|
include DateRangeHelper
|
||||||
|
attr_reader :account, :params
|
||||||
|
|
||||||
|
def initialize(account, params)
|
||||||
|
@account = account
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def metrics
|
||||||
|
{
|
||||||
|
conversation_count: bot_conversations.count,
|
||||||
|
message_count: bot_messages.count,
|
||||||
|
resolution_rate: bot_resolution_rate.to_i,
|
||||||
|
handoff_rate: bot_handoff_rate.to_i
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def bot_activated_inbox_ids
|
||||||
|
@bot_activated_inbox_ids ||= account.inboxes.filter(&:active_bot?).map(&:id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def bot_conversations
|
||||||
|
@bot_conversations ||= account.conversations.where(inbox_id: bot_activated_inbox_ids).where(created_at: range)
|
||||||
|
end
|
||||||
|
|
||||||
|
def bot_messages
|
||||||
|
@bot_messages ||= account.messages.outgoing.where(conversation_id: bot_conversations.ids).where(created_at: range)
|
||||||
|
end
|
||||||
|
|
||||||
|
def bot_resolutions_count
|
||||||
|
account.reporting_events.joins(:conversation).select(:conversation_id).where(account_id: account.id, name: :conversation_bot_resolved,
|
||||||
|
created_at: range).distinct.count
|
||||||
|
end
|
||||||
|
|
||||||
|
def bot_handoffs_count
|
||||||
|
account.reporting_events.joins(:conversation).select(:conversation_id).where(account_id: account.id, name: :conversation_bot_handoff,
|
||||||
|
created_at: range).distinct.count
|
||||||
|
end
|
||||||
|
|
||||||
|
def bot_resolution_rate
|
||||||
|
return 0 if bot_conversations.count.zero?
|
||||||
|
|
||||||
|
bot_resolutions_count.to_f / bot_conversations.count * 100
|
||||||
|
end
|
||||||
|
|
||||||
|
def bot_handoff_rate
|
||||||
|
return 0 if bot_conversations.count.zero?
|
||||||
|
|
||||||
|
bot_handoffs_count.to_f / bot_conversations.count * 100
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -19,7 +19,7 @@ class Api::V1::Accounts::AgentsController < Api::V1::Accounts::BaseController
|
|||||||
account: Current.account
|
account: Current.account
|
||||||
)
|
)
|
||||||
|
|
||||||
builder.perform
|
@agent = builder.perform
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@@ -49,6 +49,11 @@ class Api::V1::Accounts::AgentsController < Api::V1::Accounts::BaseController
|
|||||||
Rails.logger.info "[Agent#bulk_create] ignoring email #{email}, errors: #{e.record.errors}"
|
Rails.logger.info "[Agent#bulk_create] ignoring email #{email}, errors: #{e.record.errors}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# This endpoint is used to bulk create agents during onboarding
|
||||||
|
# onboarding_step key in present in Current account custom attributes, since this is a one time operation
|
||||||
|
Current.account.custom_attributes.delete('onboarding_step')
|
||||||
|
Current.account.save!
|
||||||
head :ok
|
head :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,9 @@ class Api::V1::Accounts::Channels::TwilioChannelsController < Api::V1::Accounts:
|
|||||||
before_action :authorize_request
|
before_action :authorize_request
|
||||||
|
|
||||||
def create
|
def create
|
||||||
ActiveRecord::Base.transaction do
|
process_create
|
||||||
authenticate_twilio
|
rescue StandardError => e
|
||||||
build_inbox
|
render_could_not_create_error(e.message)
|
||||||
setup_webhooks if @twilio_channel.sms?
|
|
||||||
rescue StandardError => e
|
|
||||||
render_could_not_create_error(e.message)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -17,6 +13,14 @@ class Api::V1::Accounts::Channels::TwilioChannelsController < Api::V1::Accounts:
|
|||||||
authorize ::Inbox
|
authorize ::Inbox
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def process_create
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
authenticate_twilio
|
||||||
|
build_inbox
|
||||||
|
setup_webhooks if @twilio_channel.sms?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def authenticate_twilio
|
def authenticate_twilio
|
||||||
client = if permitted_params[:api_key_sid].present?
|
client = if permitted_params[:api_key_sid].present?
|
||||||
Twilio::REST::Client.new(permitted_params[:api_key_sid], permitted_params[:auth_token], permitted_params[:account_sid])
|
Twilio::REST::Client.new(permitted_params[:api_key_sid], permitted_params[:auth_token], permitted_params[:account_sid])
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
|
|
||||||
def export
|
def export
|
||||||
column_names = params['column_names']
|
column_names = params['column_names']
|
||||||
Account::ContactsExportJob.perform_later(Current.account.id, column_names)
|
Account::ContactsExportJob.perform_later(Current.account.id, column_names, Current.user.email)
|
||||||
head :ok, message: I18n.t('errors.contacts.export.success')
|
head :ok, message: I18n.t('errors.contacts.export.success')
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -65,6 +65,10 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
contacts = result[:contacts]
|
contacts = result[:contacts]
|
||||||
@contacts_count = result[:count]
|
@contacts_count = result[:count]
|
||||||
@contacts = fetch_contacts(contacts)
|
@contacts = fetch_contacts(contacts)
|
||||||
|
rescue CustomExceptions::CustomFilter::InvalidAttribute,
|
||||||
|
CustomExceptions::CustomFilter::InvalidOperator,
|
||||||
|
CustomExceptions::CustomFilter::InvalidValue => e
|
||||||
|
render_could_not_create_error(e.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def contactable_inboxes
|
def contactable_inboxes
|
||||||
@@ -148,7 +152,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def permitted_params
|
def permitted_params
|
||||||
params.permit(:name, :identifier, :email, :phone_number, :avatar, :avatar_url, additional_attributes: {}, custom_attributes: {})
|
params.permit(:name, :identifier, :email, :phone_number, :avatar, :blocked, :avatar_url, additional_attributes: {}, custom_attributes: {})
|
||||||
end
|
end
|
||||||
|
|
||||||
def contact_custom_attributes
|
def contact_custom_attributes
|
||||||
|
|||||||
@@ -36,10 +36,18 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@conversation.update!(permitted_update_params)
|
||||||
|
end
|
||||||
|
|
||||||
def filter
|
def filter
|
||||||
result = ::Conversations::FilterService.new(params.permit!, current_user).perform
|
result = ::Conversations::FilterService.new(params.permit!, current_user).perform
|
||||||
@conversations = result[:conversations]
|
@conversations = result[:conversations]
|
||||||
@conversations_count = result[:count]
|
@conversations_count = result[:count]
|
||||||
|
rescue CustomExceptions::CustomFilter::InvalidAttribute,
|
||||||
|
CustomExceptions::CustomFilter::InvalidOperator,
|
||||||
|
CustomExceptions::CustomFilter::InvalidValue => e
|
||||||
|
render_could_not_create_error(e.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def mute
|
def mute
|
||||||
@@ -110,6 +118,11 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def permitted_update_params
|
||||||
|
# TODO: Move the other conversation attributes to this method and remove specific endpoints for each attribute
|
||||||
|
params.permit(:priority)
|
||||||
|
end
|
||||||
|
|
||||||
def update_last_seen_on_conversation(last_seen_at, update_assignee)
|
def update_last_seen_on_conversation(last_seen_at, update_assignee)
|
||||||
# rubocop:disable Rails/SkipsModelValidations
|
# rubocop:disable Rails/SkipsModelValidations
|
||||||
@conversation.update_column(:agent_last_seen_at, last_seen_at)
|
@conversation.update_column(:agent_last_seen_at, last_seen_at)
|
||||||
@@ -176,3 +189,5 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
|
|||||||
@conversation.assignee_id? && Current.user == @conversation.assignee
|
@conversation.assignee_id? && Current.user == @conversation.assignee
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Api::V1::Accounts::ConversationsController.prepend_mod_with('Api::V1::Accounts::ConversationsController')
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseContro
|
|||||||
before_action :set_current_page, only: [:index]
|
before_action :set_current_page, only: [:index]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@notifications = notification_finder.notifications
|
||||||
@unread_count = notification_finder.unread_count
|
@unread_count = notification_finder.unread_count
|
||||||
@notifications = notification_finder.perform
|
|
||||||
@count = notification_finder.count
|
@count = notification_finder.count
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -54,7 +54,8 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseContro
|
|||||||
end
|
end
|
||||||
|
|
||||||
def snooze
|
def snooze
|
||||||
@notification.update(snoozed_until: parse_date_time(params[:snoozed_until].to_s)) if params[:snoozed_until]
|
updated_meta = (@notification.meta || {}).merge('last_snoozed_at' => nil)
|
||||||
|
@notification.update(snoozed_until: parse_date_time(params[:snoozed_until].to_s), meta: updated_meta) if params[:snoozed_until]
|
||||||
render json: @notification
|
render json: @notification
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class Api::V1::AccountsController < Api::BaseController
|
|||||||
def update
|
def update
|
||||||
@account.assign_attributes(account_params.slice(:name, :locale, :domain, :support_email, :auto_resolve_duration))
|
@account.assign_attributes(account_params.slice(:name, :locale, :domain, :support_email, :auto_resolve_duration))
|
||||||
@account.custom_attributes.merge!(custom_attributes_params)
|
@account.custom_attributes.merge!(custom_attributes_params)
|
||||||
|
@account.custom_attributes['onboarding_step'] = 'invite_team' if @account.custom_attributes['onboarding_step'] == 'account_update'
|
||||||
@account.save!
|
@account.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ class Api::V1::Widget::ConversationsController < Api::V1::Widget::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def transcript
|
def transcript
|
||||||
if permitted_params[:email].present? && conversation.present?
|
if conversation.present? && conversation.contact.present? && conversation.contact.email.present?
|
||||||
ConversationReplyMailer.with(account: conversation.account).conversation_transcript(
|
ConversationReplyMailer.with(account: conversation.account).conversation_transcript(
|
||||||
conversation,
|
conversation,
|
||||||
permitted_params[:email]
|
conversation.contact.email
|
||||||
)&.deliver_later
|
)&.deliver_later
|
||||||
end
|
end
|
||||||
head :ok
|
head :ok
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
|||||||
render json: summary_metrics
|
render json: summary_metrics
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def bot_summary
|
||||||
|
summary = V2::ReportBuilder.new(Current.account, current_summary_params).bot_summary
|
||||||
|
summary[:previous] = V2::ReportBuilder.new(Current.account, previous_summary_params).bot_summary
|
||||||
|
render json: summary
|
||||||
|
end
|
||||||
|
|
||||||
def agents
|
def agents
|
||||||
@report_data = generate_agents_report
|
@report_data = generate_agents_report
|
||||||
generate_csv('agents_report', 'api/v2/accounts/reports/agents')
|
generate_csv('agents_report', 'api/v2/accounts/reports/agents')
|
||||||
@@ -48,6 +54,11 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
|||||||
render json: conversation_metrics
|
render json: conversation_metrics
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def bot_metrics
|
||||||
|
bot_metrics = V2::Reports::BotMetricsBuilder.new(Current.account, params).metrics
|
||||||
|
render json: bot_metrics
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def generate_csv(filename, template)
|
def generate_csv(filename, template)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class Api::V2::AccountsController < Api::BaseController
|
|||||||
).perform
|
).perform
|
||||||
|
|
||||||
fetch_account_and_user_info
|
fetch_account_and_user_info
|
||||||
|
update_account_info if @account.present?
|
||||||
|
|
||||||
if @user
|
if @user
|
||||||
send_auth_headers(@user)
|
send_auth_headers(@user)
|
||||||
@@ -33,6 +34,18 @@ class Api::V2::AccountsController < Api::BaseController
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def account_attributes
|
||||||
|
{
|
||||||
|
custom_attributes: @account.custom_attributes.merge({ 'onboarding_step' => 'profile_update' })
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_account_info
|
||||||
|
@account.update!(
|
||||||
|
account_attributes
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def fetch_account_and_user_info; end
|
def fetch_account_and_user_info; end
|
||||||
|
|
||||||
def fetch_account
|
def fetch_account
|
||||||
|
|||||||
@@ -25,3 +25,4 @@ class ApplicationController < ActionController::Base
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
ApplicationController.include_mod_with('Concerns::ApplicationControllerConcern')
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module AccessTokenAuthHelper
|
module AccessTokenAuthHelper
|
||||||
BOT_ACCESSIBLE_ENDPOINTS = {
|
BOT_ACCESSIBLE_ENDPOINTS = {
|
||||||
'api/v1/accounts/conversations' => %w[toggle_status toggle_priority create],
|
'api/v1/accounts/conversations' => %w[toggle_status toggle_priority create update],
|
||||||
'api/v1/accounts/conversations/messages' => ['create'],
|
'api/v1/accounts/conversations/messages' => ['create'],
|
||||||
'api/v1/accounts/conversations/assignments' => ['create']
|
'api/v1/accounts/conversations/assignments' => ['create']
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
module DomainHelper
|
||||||
|
def self.chatwoot_domain?(domain = request.host)
|
||||||
|
[URI.parse(ENV.fetch('FRONTEND_URL', '')).host, URI.parse(ENV.fetch('HELPCENTER_URL', '')).host].include?(domain)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -6,6 +6,7 @@ module SwitchLocale
|
|||||||
def switch_locale(&)
|
def switch_locale(&)
|
||||||
# priority is for locale set in query string (mostly for widget/from js sdk)
|
# priority is for locale set in query string (mostly for widget/from js sdk)
|
||||||
locale ||= locale_from_params
|
locale ||= locale_from_params
|
||||||
|
locale ||= locale_from_custom_domain
|
||||||
# if locale is not set in account, let's use DEFAULT_LOCALE env variable
|
# if locale is not set in account, let's use DEFAULT_LOCALE env variable
|
||||||
locale ||= locale_from_env_variable
|
locale ||= locale_from_env_variable
|
||||||
set_locale(locale, &)
|
set_locale(locale, &)
|
||||||
@@ -16,6 +17,20 @@ module SwitchLocale
|
|||||||
set_locale(locale, &)
|
set_locale(locale, &)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If the request is coming from a custom domain, it should be for a helpcenter portal
|
||||||
|
# We will use the portal locale in such cases
|
||||||
|
def locale_from_custom_domain(&)
|
||||||
|
return if params[:locale]
|
||||||
|
|
||||||
|
domain = request.host
|
||||||
|
return if DomainHelper.chatwoot_domain?(domain)
|
||||||
|
|
||||||
|
@portal = Portal.find_by(custom_domain: domain)
|
||||||
|
return unless @portal
|
||||||
|
|
||||||
|
@portal.default_locale
|
||||||
|
end
|
||||||
|
|
||||||
def set_locale(locale, &)
|
def set_locale(locale, &)
|
||||||
# if locale is empty, use default_locale
|
# if locale is empty, use default_locale
|
||||||
locale ||= I18n.default_locale
|
locale ||= I18n.default_locale
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class DashboardController < ActionController::Base
|
|||||||
'LOGO', 'LOGO_DARK', 'LOGO_THUMBNAIL',
|
'LOGO', 'LOGO_DARK', 'LOGO_THUMBNAIL',
|
||||||
'INSTALLATION_NAME',
|
'INSTALLATION_NAME',
|
||||||
'WIDGET_BRAND_URL', 'TERMS_URL',
|
'WIDGET_BRAND_URL', 'TERMS_URL',
|
||||||
|
'BRAND_URL', 'BRAND_NAME',
|
||||||
'PRIVACY_URL',
|
'PRIVACY_URL',
|
||||||
'DISPLAY_MANIFEST',
|
'DISPLAY_MANIFEST',
|
||||||
'CREATE_NEW_ACCOUNT_FROM_DASHBOARD',
|
'CREATE_NEW_ACCOUNT_FROM_DASHBOARD',
|
||||||
@@ -55,7 +56,7 @@ class DashboardController < ActionController::Base
|
|||||||
VAPID_PUBLIC_KEY: VapidService.public_key,
|
VAPID_PUBLIC_KEY: VapidService.public_key,
|
||||||
ENABLE_ACCOUNT_SIGNUP: GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false'),
|
ENABLE_ACCOUNT_SIGNUP: GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false'),
|
||||||
FB_APP_ID: GlobalConfigService.load('FB_APP_ID', ''),
|
FB_APP_ID: GlobalConfigService.load('FB_APP_ID', ''),
|
||||||
FACEBOOK_API_VERSION: 'v14.0',
|
FACEBOOK_API_VERSION: GlobalConfigService.load('FACEBOOK_API_VERSION', 'v17.0'),
|
||||||
IS_ENTERPRISE: ChatwootApp.enterprise?,
|
IS_ENTERPRISE: ChatwootApp.enterprise?,
|
||||||
AZURE_APP_ID: ENV.fetch('AZURE_APP_ID', ''),
|
AZURE_APP_ID: ENV.fetch('AZURE_APP_ID', ''),
|
||||||
GIT_SHA: GIT_HASH
|
GIT_SHA: GIT_HASH
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ class DeviseOverrides::PasswordsController < Devise::PasswordsController
|
|||||||
skip_before_action :authenticate_user!, raise: false
|
skip_before_action :authenticate_user!, raise: false
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@user = User.find_by(email: params[:email])
|
@user = User.from_email(params[:email])
|
||||||
if @user
|
if @user
|
||||||
@user.send_reset_password_instructions
|
@user.send_reset_password_instructions
|
||||||
build_response(I18n.t('messages.reset_password_success'), 200)
|
build_response(I18n.t('messages.reset_password_success'), 200)
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ class DeviseOverrides::SessionsController < DeviseTokenAuth::SessionsController
|
|||||||
wrap_parameters format: []
|
wrap_parameters format: []
|
||||||
before_action :process_sso_auth_token, only: [:create]
|
before_action :process_sso_auth_token, only: [:create]
|
||||||
|
|
||||||
|
def new
|
||||||
|
redirect_to login_page_url(error: 'access-denied')
|
||||||
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
# Authenticate user via the temporary sso auth token
|
# Authenticate user via the temporary sso auth token
|
||||||
if params[:sso_auth_token].present? && @resource.present?
|
if params[:sso_auth_token].present? && @resource.present?
|
||||||
@@ -21,6 +25,12 @@ class DeviseOverrides::SessionsController < DeviseTokenAuth::SessionsController
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def login_page_url(error: nil)
|
||||||
|
frontend_url = ENV.fetch('FRONTEND_URL', nil)
|
||||||
|
|
||||||
|
"#{frontend_url}/app/login?error=#{error}"
|
||||||
|
end
|
||||||
|
|
||||||
def authenticate_resource_with_sso_token
|
def authenticate_resource_with_sso_token
|
||||||
@token = @resource.create_token
|
@token = @resource.create_token
|
||||||
@resource.save!
|
@resource.save!
|
||||||
@@ -33,7 +43,7 @@ class DeviseOverrides::SessionsController < DeviseTokenAuth::SessionsController
|
|||||||
def process_sso_auth_token
|
def process_sso_auth_token
|
||||||
return if params[:email].blank?
|
return if params[:email].blank?
|
||||||
|
|
||||||
user = User.find_by(email: params[:email])
|
user = User.from_email(params[:email])
|
||||||
@resource = user if user&.valid_sso_auth_token?(params[:sso_auth_token])
|
@resource = user if user&.valid_sso_auth_token?(params[:sso_auth_token])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class Platform::Api::V1::UsersController < PlatformController
|
|||||||
def show; end
|
def show; end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@resource = (User.find_by(email: user_params[:email]) || User.new(user_params))
|
@resource = (User.from_email(user_params[:email]) || User.new(user_params))
|
||||||
@resource.skip_confirmation!
|
@resource.skip_confirmation!
|
||||||
@resource.save!
|
@resource.save!
|
||||||
@platform_app.platform_app_permissibles.find_or_create_by!(permissible: @resource)
|
@platform_app.platform_app_permissibles.find_or_create_by!(permissible: @resource)
|
||||||
|
|||||||
@@ -1,15 +1,31 @@
|
|||||||
class Public::Api::V1::Inboxes::ConversationsController < Public::Api::V1::InboxesController
|
class Public::Api::V1::Inboxes::ConversationsController < Public::Api::V1::InboxesController
|
||||||
include Events::Types
|
include Events::Types
|
||||||
before_action :set_conversation, only: [:toggle_typing, :update_last_seen]
|
before_action :set_conversation, only: [:toggle_typing, :update_last_seen, :show, :toggle_status]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@conversations = @contact_inbox.hmac_verified? ? @contact.conversations : @contact_inbox.conversations
|
@conversations = @contact_inbox.hmac_verified? ? @contact.conversations : @contact_inbox.conversations
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def show; end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@conversation = create_conversation
|
@conversation = create_conversation
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def toggle_status
|
||||||
|
# Check if the conversation is already resolved to prevent redundant operations
|
||||||
|
return if @conversation.resolved?
|
||||||
|
|
||||||
|
# Assign the conversation's contact as the resolver
|
||||||
|
# This step attributes the resolution action to the contact involved in the conversation
|
||||||
|
# If this assignment is not made, the system implicitly becomes the resolver by default
|
||||||
|
Current.contact = @conversation.contact
|
||||||
|
|
||||||
|
# Update the conversation's status to 'resolved' to reflect its closure
|
||||||
|
@conversation.status = :resolved
|
||||||
|
@conversation.save!
|
||||||
|
end
|
||||||
|
|
||||||
def toggle_typing
|
def toggle_typing
|
||||||
case params[:typing_status]
|
case params[:typing_status]
|
||||||
when 'on'
|
when 'on'
|
||||||
@@ -30,7 +46,11 @@ class Public::Api::V1::Inboxes::ConversationsController < Public::Api::V1::Inbox
|
|||||||
private
|
private
|
||||||
|
|
||||||
def set_conversation
|
def set_conversation
|
||||||
@conversation = @contact_inbox.contact.conversations.find_by!(display_id: params[:id])
|
@conversation = if @contact_inbox.hmac_verified?
|
||||||
|
@contact_inbox.contact.conversations.find_by!(display_id: params[:id])
|
||||||
|
else
|
||||||
|
@contact_inbox.conversations.find_by!(display_id: params[:id])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_conversation
|
def create_conversation
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class Public::Api::V1::Portals::ArticlesController < Public::Api::V1::Portals::B
|
|||||||
|
|
||||||
def index
|
def index
|
||||||
@articles = @portal.articles
|
@articles = @portal.articles
|
||||||
@articles = @articles.search(list_params) if list_params.present?
|
search_articles
|
||||||
order_by_sort_param
|
order_by_sort_param
|
||||||
@articles.page(list_params[:page]) if list_params[:page].present?
|
@articles.page(list_params[:page]) if list_params[:page].present?
|
||||||
end
|
end
|
||||||
@@ -16,6 +16,10 @@ class Public::Api::V1::Portals::ArticlesController < Public::Api::V1::Portals::B
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def search_articles
|
||||||
|
@articles = @articles.search(list_params) if list_params.present?
|
||||||
|
end
|
||||||
|
|
||||||
def order_by_sort_param
|
def order_by_sort_param
|
||||||
@articles = if list_params[:sort].present? && list_params[:sort] == 'views'
|
@articles = if list_params[:sort].present? && list_params[:sort] == 'views'
|
||||||
@articles.order_by_views
|
@articles.order_by_views
|
||||||
@@ -51,3 +55,5 @@ class Public::Api::V1::Portals::ArticlesController < Public::Api::V1::Portals::B
|
|||||||
ChatwootMarkdownRenderer.new(content).render_article
|
ChatwootMarkdownRenderer.new(content).render_article
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Public::Api::V1::Portals::ArticlesController.prepend_mod_with('Public::Api::V1::Portals::ArticlesController')
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class Public::Api::V1::Portals::BaseController < PublicController
|
|||||||
@locale = if article.category.present?
|
@locale = if article.category.present?
|
||||||
article.category.locale
|
article.category.locale
|
||||||
else
|
else
|
||||||
'en'
|
article.portal.default_locale
|
||||||
end
|
end
|
||||||
|
|
||||||
I18n.with_locale(@locale, &)
|
I18n.with_locale(@locale, &)
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ class Public::Api::V1::PortalsController < Public::Api::V1::Portals::BaseControl
|
|||||||
|
|
||||||
def show; end
|
def show; end
|
||||||
|
|
||||||
|
def sitemap
|
||||||
|
@help_center_url = @portal.custom_domain || ChatwootApp.help_center_root
|
||||||
|
# if help_center_url does not contain a protocol, prepend it with https
|
||||||
|
@help_center_url = "https://#{@help_center_url}" unless @help_center_url.include?('://')
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def portal
|
def portal
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ class PublicController < ActionController::Base
|
|||||||
|
|
||||||
def ensure_custom_domain_request
|
def ensure_custom_domain_request
|
||||||
domain = request.host
|
domain = request.host
|
||||||
|
return if DomainHelper.chatwoot_domain?(domain)
|
||||||
return if [URI.parse(ENV.fetch('FRONTEND_URL', '')).host, URI.parse(ENV.fetch('HELPCENTER_URL', '')).host].include?(domain)
|
|
||||||
|
|
||||||
@portal = ::Portal.find_by(custom_domain: domain)
|
@portal = ::Portal.find_by(custom_domain: domain)
|
||||||
return if @portal.present?
|
return if @portal.present?
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class SuperAdmin::AppConfigsController < SuperAdmin::ApplicationController
|
|||||||
def allowed_configs
|
def allowed_configs
|
||||||
@allowed_configs = case @config
|
@allowed_configs = case @config
|
||||||
when 'facebook'
|
when 'facebook'
|
||||||
%w[FB_APP_ID FB_VERIFY_TOKEN FB_APP_SECRET IG_VERIFY_TOKEN ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT]
|
%w[FB_APP_ID FB_VERIFY_TOKEN FB_APP_SECRET IG_VERIFY_TOKEN FACEBOOK_API_VERSION ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT]
|
||||||
when 'email'
|
when 'email'
|
||||||
['MAILER_INBOUND_EMAIL_DOMAIN']
|
['MAILER_INBOUND_EMAIL_DOMAIN']
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
# If you want to add pagination or other controller-level concerns,
|
# If you want to add pagination or other controller-level concerns,
|
||||||
# you're free to overwrite the RESTful controller actions.
|
# you're free to overwrite the RESTful controller actions.
|
||||||
class SuperAdmin::ApplicationController < Administrate::ApplicationController
|
class SuperAdmin::ApplicationController < Administrate::ApplicationController
|
||||||
|
include ActionView::Helpers::TagHelper
|
||||||
|
include ActionView::Context
|
||||||
|
|
||||||
|
helper_method :render_vue_component
|
||||||
# authenticiation done via devise : SuperAdmin Model
|
# authenticiation done via devise : SuperAdmin Model
|
||||||
before_action :authenticate_super_admin!
|
before_action :authenticate_super_admin!
|
||||||
|
|
||||||
@@ -23,6 +27,17 @@ class SuperAdmin::ApplicationController < Administrate::ApplicationController
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def render_vue_component(component_name, props = {})
|
||||||
|
html_options = {
|
||||||
|
id: 'app',
|
||||||
|
data: {
|
||||||
|
component_name: component_name,
|
||||||
|
props: props.to_json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content_tag(:div, '', html_options)
|
||||||
|
end
|
||||||
|
|
||||||
def invalid_action_perfomed
|
def invalid_action_perfomed
|
||||||
# rubocop:disable Rails/I18nLocaleTexts
|
# rubocop:disable Rails/I18nLocaleTexts
|
||||||
flash[:error] = 'Invalid action performed'
|
flash[:error] = 'Invalid action performed'
|
||||||
|
|||||||
@@ -163,12 +163,23 @@ class ConversationFinder
|
|||||||
params[:page] || 1
|
params[:page] || 1
|
||||||
end
|
end
|
||||||
|
|
||||||
def conversations
|
def conversations_base_query
|
||||||
@conversations = @conversations.includes(
|
@conversations.includes(
|
||||||
:taggings, :inbox, { assignee: { avatar_attachment: [:blob] } }, { contact: { avatar_attachment: [:blob] } }, :team, :contact_inbox
|
:taggings, :inbox, { assignee: { avatar_attachment: [:blob] } }, { contact: { avatar_attachment: [:blob] } }, :team, :contact_inbox
|
||||||
)
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversations
|
||||||
|
@conversations = conversations_base_query
|
||||||
|
|
||||||
sort_by, sort_order = SORT_OPTIONS[params[:sort_by]] || SORT_OPTIONS['last_activity_at_desc']
|
sort_by, sort_order = SORT_OPTIONS[params[:sort_by]] || SORT_OPTIONS['last_activity_at_desc']
|
||||||
@conversations.send(sort_by, sort_order).page(current_page).per(ENV.fetch('CONVERSATION_RESULTS_PER_PAGE', '25').to_i)
|
@conversations = @conversations.send(sort_by, sort_order)
|
||||||
|
|
||||||
|
if params[:updated_within].present?
|
||||||
|
@conversations.where('conversations.updated_at > ?', Time.zone.now - params[:updated_within].to_i.seconds)
|
||||||
|
else
|
||||||
|
@conversations.page(current_page).per(ENV.fetch('CONVERSATION_RESULTS_PER_PAGE', '25').to_i)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
ConversationFinder.prepend_mod_with('ConversationFinder')
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ class NotificationFinder
|
|||||||
set_up
|
set_up
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform
|
def notifications
|
||||||
notifications
|
@notifications.page(current_page).per(RESULTS_PER_PAGE).order(last_activity_at: sort_order)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unread_count
|
def unread_count
|
||||||
@@ -26,27 +26,31 @@ class NotificationFinder
|
|||||||
|
|
||||||
def set_up
|
def set_up
|
||||||
find_all_notifications
|
find_all_notifications
|
||||||
filter_by_read_status
|
filter_snoozed_notifications
|
||||||
filter_by_status
|
fitler_read_notifications
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_all_notifications
|
def find_all_notifications
|
||||||
@notifications = current_user.notifications.where(account_id: @current_account.id)
|
@notifications = current_user.notifications.where(account_id: @current_account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_by_status
|
def filter_snoozed_notifications
|
||||||
@notifications = @notifications.where('snoozed_until > ?', DateTime.now.utc) if params[:status] == 'snoozed'
|
@notifications = @notifications.where(snoozed_until: nil) unless type_included?('snoozed')
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_by_read_status
|
def fitler_read_notifications
|
||||||
@notifications = @notifications.where.not(read_at: nil) if params[:type] == 'read'
|
@notifications = @notifications.where(read_at: nil) unless type_included?('read')
|
||||||
|
end
|
||||||
|
|
||||||
|
def type_included?(type)
|
||||||
|
(params[:includes] || []).include?(type)
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_page
|
def current_page
|
||||||
params[:page] || 1
|
params[:page] || 1
|
||||||
end
|
end
|
||||||
|
|
||||||
def notifications
|
def sort_order
|
||||||
@notifications.page(current_page).per(RESULTS_PER_PAGE).order(last_activity_at: params[:sort_order] || :desc)
|
params[:sort_order] || :desc
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,4 +2,11 @@ module ApplicationHelper
|
|||||||
def available_locales_with_name
|
def available_locales_with_name
|
||||||
LANGUAGES_CONFIG.map { |_key, val| val.slice(:name, :iso_639_1_code) }
|
LANGUAGES_CONFIG.map { |_key, val| val.slice(:name, :iso_639_1_code) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def feature_help_urls
|
||||||
|
features = YAML.safe_load(Rails.root.join('config/features.yml').read).freeze
|
||||||
|
features.each_with_object({}) do |feature, hash|
|
||||||
|
hash[feature['name']] = feature['help_url'] if feature['help_url']
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
module ContactHelper
|
||||||
|
def parse_name(full_name)
|
||||||
|
# If the input is nil or not a string, return a hash with all values set to nil
|
||||||
|
return default_name_hash if invalid_name?(full_name)
|
||||||
|
|
||||||
|
# If the input is a number, return a hash with the number as the first name
|
||||||
|
return numeric_name_hash(full_name) if valid_number?(full_name)
|
||||||
|
|
||||||
|
full_name = full_name.squish
|
||||||
|
|
||||||
|
# If full name consists of only one word, consider it as the first name
|
||||||
|
return single_word_name_hash(full_name) if single_word?(full_name)
|
||||||
|
|
||||||
|
parts = split_name(full_name)
|
||||||
|
parts = handle_conjunction(parts)
|
||||||
|
build_name_hash(parts)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def default_name_hash
|
||||||
|
{ first_name: nil, last_name: nil, middle_name: nil, prefix: nil, suffix: nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
def invalid_name?(full_name)
|
||||||
|
!full_name.is_a?(String) || full_name.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def numeric_name_hash(full_name)
|
||||||
|
{ first_name: full_name, last_name: nil, middle_name: nil, prefix: nil, suffix: nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_number?(full_name)
|
||||||
|
full_name.gsub(/\s+/, '').match?(/\A\+?\d+\z/)
|
||||||
|
end
|
||||||
|
|
||||||
|
def single_word_name_hash(full_name)
|
||||||
|
{ first_name: full_name, last_name: nil, middle_name: nil, prefix: nil, suffix: nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
def single_word?(full_name)
|
||||||
|
full_name.split.size == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def split_name(full_name)
|
||||||
|
full_name.split
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_conjunction(parts)
|
||||||
|
conjunctions = ['and', '&']
|
||||||
|
parts.each_index do |i|
|
||||||
|
next unless conjunctions.include?(parts[i]) && i.positive?
|
||||||
|
|
||||||
|
parts[i - 1] = [parts[i - 1], parts[i + 1]].join(' ')
|
||||||
|
parts.delete_at(i)
|
||||||
|
parts.delete_at(i)
|
||||||
|
end
|
||||||
|
parts
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_name_hash(parts)
|
||||||
|
suffix = parts.pop if parts.last.match?(/(\w+\.|[IVXLM]+|[A-Z]+)$/)
|
||||||
|
last_name = parts.pop
|
||||||
|
prefix = parts.shift if parts.first.match?(/^\w+\./)
|
||||||
|
first_name = parts.shift
|
||||||
|
middle_name = parts.join(' ')
|
||||||
|
|
||||||
|
hash = {
|
||||||
|
first_name: first_name,
|
||||||
|
last_name: last_name,
|
||||||
|
prefix: prefix,
|
||||||
|
middle_name: middle_name,
|
||||||
|
suffix: suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reverse name if "," was used in Last, First notation.
|
||||||
|
if hash[:first_name] =~ /,$/
|
||||||
|
hash[:first_name] = hash[:last_name]
|
||||||
|
hash[:last_name] = Regexp.last_match.pre_match
|
||||||
|
end
|
||||||
|
hash
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
module FilterHelper
|
||||||
|
def build_condition_query(model_filters, query_hash, current_index)
|
||||||
|
current_filter = model_filters[query_hash['attribute_key']]
|
||||||
|
|
||||||
|
# Throw InvalidOperator Error if the attribute is a standard attribute
|
||||||
|
# and the operator is not allowed in the config
|
||||||
|
if current_filter.present? && current_filter['filter_operators'].exclude?(query_hash[:filter_operator])
|
||||||
|
raise CustomExceptions::CustomFilter::InvalidOperator.new(
|
||||||
|
attribute_name: query_hash['attribute_key'],
|
||||||
|
allowed_keys: current_filter['filter_operators']
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Every other filter expects a value to be present
|
||||||
|
if %w[is_present is_not_present].exclude?(query_hash[:filter_operator]) && query_hash['values'].blank?
|
||||||
|
raise CustomExceptions::CustomFilter::InvalidValue.new(attribute_name: query_hash['attribute_key'])
|
||||||
|
end
|
||||||
|
|
||||||
|
condition_query = build_condition_query_string(current_filter, query_hash, current_index)
|
||||||
|
# The query becomes empty only when it doesn't match to any supported
|
||||||
|
# standard attribute or custom attribute defined in the account.
|
||||||
|
if condition_query.empty?
|
||||||
|
raise CustomExceptions::CustomFilter::InvalidAttribute.new(key: query_hash['attribute_key'],
|
||||||
|
allowed_keys: model_filters.keys)
|
||||||
|
end
|
||||||
|
|
||||||
|
condition_query
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_condition_query_string(current_filter, query_hash, current_index)
|
||||||
|
filter_operator_value = filter_operation(query_hash, current_index)
|
||||||
|
|
||||||
|
return handle_nil_filter(query_hash, current_index) if current_filter.nil?
|
||||||
|
|
||||||
|
case current_filter['attribute_type']
|
||||||
|
when 'additional_attributes'
|
||||||
|
handle_additional_attributes(query_hash, filter_operator_value, current_filter['data_type'])
|
||||||
|
else
|
||||||
|
handle_standard_attributes(current_filter, query_hash, current_index, filter_operator_value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_nil_filter(query_hash, current_index)
|
||||||
|
attribute_type = "#{filter_config[:entity].downcase}_attribute"
|
||||||
|
custom_attribute_query(query_hash, attribute_type, current_index)
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_additional_attributes(query_hash, filter_operator_value, data_type)
|
||||||
|
if data_type == 'text_case_insensitive'
|
||||||
|
"LOWER(#{filter_config[:table_name]}.additional_attributes ->> '#{query_hash[:attribute_key]}') " \
|
||||||
|
"#{filter_operator_value} #{query_hash[:query_operator]}"
|
||||||
|
else
|
||||||
|
"#{filter_config[:table_name]}.additional_attributes ->> '#{query_hash[:attribute_key]}' " \
|
||||||
|
"#{filter_operator_value} #{query_hash[:query_operator]} "
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_standard_attributes(current_filter, query_hash, current_index, filter_operator_value)
|
||||||
|
case current_filter['data_type']
|
||||||
|
when 'date'
|
||||||
|
date_filter(current_filter, query_hash, filter_operator_value)
|
||||||
|
when 'labels'
|
||||||
|
tag_filter_query(query_hash, current_index)
|
||||||
|
when 'text_case_insensitive'
|
||||||
|
text_case_insensitive_filter(query_hash, filter_operator_value)
|
||||||
|
else
|
||||||
|
default_filter(query_hash, filter_operator_value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def date_filter(current_filter, query_hash, filter_operator_value)
|
||||||
|
"(#{filter_config[:table_name]}.#{query_hash[:attribute_key]})::#{current_filter['data_type']} " \
|
||||||
|
"#{filter_operator_value}#{current_filter['data_type']} #{query_hash[:query_operator]}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def text_case_insensitive_filter(query_hash, filter_operator_value)
|
||||||
|
"LOWER(#{filter_config[:table_name]}.#{query_hash[:attribute_key]}) " \
|
||||||
|
"#{filter_operator_value} #{query_hash[:query_operator]}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_filter(query_hash, filter_operator_value)
|
||||||
|
"#{filter_config[:table_name]}.#{query_hash[:attribute_key]} #{filter_operator_value} #{query_hash[:query_operator]}"
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -32,6 +32,14 @@ module ReportHelper
|
|||||||
(get_grouped_values resolutions).count
|
(get_grouped_values resolutions).count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def bot_resolutions_count
|
||||||
|
(get_grouped_values bot_resolutions).count
|
||||||
|
end
|
||||||
|
|
||||||
|
def bot_handoffs_count
|
||||||
|
(get_grouped_values bot_handoffs).count
|
||||||
|
end
|
||||||
|
|
||||||
def conversations
|
def conversations
|
||||||
scope.conversations.where(account_id: account.id, created_at: range)
|
scope.conversations.where(account_id: account.id, created_at: range)
|
||||||
end
|
end
|
||||||
@@ -49,6 +57,16 @@ module ReportHelper
|
|||||||
conversations: { status: :resolved }, created_at: range).distinct
|
conversations: { status: :resolved }, created_at: range).distinct
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def bot_resolutions
|
||||||
|
scope.reporting_events.joins(:conversation).select(:conversation_id).where(account_id: account.id, name: :conversation_bot_resolved,
|
||||||
|
conversations: { status: :resolved }, created_at: range).distinct
|
||||||
|
end
|
||||||
|
|
||||||
|
def bot_handoffs
|
||||||
|
scope.reporting_events.joins(:conversation).select(:conversation_id).where(account_id: account.id, name: :conversation_bot_handoff,
|
||||||
|
created_at: range).distinct
|
||||||
|
end
|
||||||
|
|
||||||
def avg_first_response_time
|
def avg_first_response_time
|
||||||
grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'first_response', account_id: account.id))
|
grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'first_response', account_id: account.id))
|
||||||
return grouped_reporting_events.average(:value_in_business_hours) if params[:business_hours]
|
return grouped_reporting_events.average(:value_in_business_hours) if params[:business_hours]
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
>
|
>
|
||||||
<update-banner :latest-chatwoot-version="latestChatwootVersion" />
|
<update-banner :latest-chatwoot-version="latestChatwootVersion" />
|
||||||
<template v-if="currentAccountId">
|
<template v-if="currentAccountId">
|
||||||
<pending-email-verification-banner />
|
<pending-email-verification-banner v-if="hideOnOnboardingView" />
|
||||||
<payment-pending-banner />
|
<payment-pending-banner v-if="hideOnOnboardingView" />
|
||||||
<upgrade-banner />
|
<upgrade-banner />
|
||||||
</template>
|
</template>
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
@@ -38,6 +38,7 @@ import vueActionCable from './helper/actionCable';
|
|||||||
import WootSnackbarBox from './components/SnackbarContainer.vue';
|
import WootSnackbarBox from './components/SnackbarContainer.vue';
|
||||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||||
import { setColorTheme } from './helper/themeHelper';
|
import { setColorTheme } from './helper/themeHelper';
|
||||||
|
import { isOnOnboardingView } from 'v3/helpers/RouteHelper';
|
||||||
import {
|
import {
|
||||||
registerSubscription,
|
registerSubscription,
|
||||||
verifyServiceWorkerExistence,
|
verifyServiceWorkerExistence,
|
||||||
@@ -79,6 +80,9 @@ export default {
|
|||||||
const { accounts = [] } = this.currentUser || {};
|
const { accounts = [] } = this.currentUser || {};
|
||||||
return accounts.length > 0;
|
return accounts.length > 0;
|
||||||
},
|
},
|
||||||
|
hideOnOnboardingView() {
|
||||||
|
return !isOnOnboardingView(this.$route);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
|
/* global axios */
|
||||||
|
|
||||||
import ApiClient from './ApiClient';
|
import ApiClient from './ApiClient';
|
||||||
|
|
||||||
class Agents extends ApiClient {
|
class Agents extends ApiClient {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('agents', { accountScoped: true });
|
super('agents', { accountScoped: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bulkInvite({ emails }) {
|
||||||
|
return axios.post(`${this.url}/bulk_create`, {
|
||||||
|
emails,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Agents();
|
export default new Agents();
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ class NotificationsAPI extends ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get({ page, status, type, sortOrder }) {
|
get({ page, status, type, sortOrder }) {
|
||||||
|
const includesFilter = [status, type].filter(value => !!value);
|
||||||
|
|
||||||
return axios.get(this.url, {
|
return axios.get(this.url, {
|
||||||
params: {
|
params: {
|
||||||
page,
|
page,
|
||||||
status,
|
|
||||||
type,
|
|
||||||
sort_order: sortOrder,
|
sort_order: sortOrder,
|
||||||
|
includes: includesFilter,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,24 @@ class ReportsAPI extends ApiClient {
|
|||||||
params: { since, until, business_hours: businessHours },
|
params: { since, until, business_hours: businessHours },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBotMetrics({ from, to } = {}) {
|
||||||
|
return axios.get(`${this.url}/bot_metrics`, {
|
||||||
|
params: { since: from, until: to },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getBotSummary({ from, to, groupBy, businessHours } = {}) {
|
||||||
|
return axios.get(`${this.url}/bot_summary`, {
|
||||||
|
params: {
|
||||||
|
since: from,
|
||||||
|
until: to,
|
||||||
|
type: 'account',
|
||||||
|
group_by: groupBy,
|
||||||
|
business_hours: businessHours,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new ReportsAPI();
|
export default new ReportsAPI();
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import ApiClient from './ApiClient';
|
||||||
|
|
||||||
|
class SlaAPI extends ApiClient {
|
||||||
|
constructor() {
|
||||||
|
super('sla_policies', { accountScoped: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new SlaAPI();
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/* global axios */
|
||||||
|
import ApiClient from './ApiClient';
|
||||||
|
|
||||||
|
class SLAReportsAPI extends ApiClient {
|
||||||
|
constructor() {
|
||||||
|
super('applied_slas', { accountScoped: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
get({
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
assigned_agent_id,
|
||||||
|
inbox_id,
|
||||||
|
team_id,
|
||||||
|
sla_policy_id,
|
||||||
|
label_list,
|
||||||
|
page,
|
||||||
|
} = {}) {
|
||||||
|
return axios.get(this.url, {
|
||||||
|
params: {
|
||||||
|
since: from,
|
||||||
|
until: to,
|
||||||
|
assigned_agent_id,
|
||||||
|
inbox_id,
|
||||||
|
team_id,
|
||||||
|
sla_policy_id,
|
||||||
|
label_list,
|
||||||
|
page,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
download({
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
assigned_agent_id,
|
||||||
|
inbox_id,
|
||||||
|
team_id,
|
||||||
|
sla_policy_id,
|
||||||
|
label_list,
|
||||||
|
} = {}) {
|
||||||
|
return axios.get(`${this.url}/download`, {
|
||||||
|
params: {
|
||||||
|
since: from,
|
||||||
|
until: to,
|
||||||
|
assigned_agent_id,
|
||||||
|
inbox_id,
|
||||||
|
team_id,
|
||||||
|
label_list,
|
||||||
|
sla_policy_id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getMetrics({
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
assigned_agent_id,
|
||||||
|
inbox_id,
|
||||||
|
team_id,
|
||||||
|
label_list,
|
||||||
|
sla_policy_id,
|
||||||
|
} = {}) {
|
||||||
|
return axios.get(`${this.url}/metrics`, {
|
||||||
|
params: {
|
||||||
|
since: from,
|
||||||
|
until: to,
|
||||||
|
assigned_agent_id,
|
||||||
|
inbox_id,
|
||||||
|
label_list,
|
||||||
|
team_id,
|
||||||
|
sla_policy_id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new SLAReportsAPI();
|
||||||
@@ -10,4 +10,29 @@ describe('#AgentAPI', () => {
|
|||||||
expect(agents).toHaveProperty('update');
|
expect(agents).toHaveProperty('update');
|
||||||
expect(agents).toHaveProperty('delete');
|
expect(agents).toHaveProperty('delete');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#bulkInvite', () => {
|
||||||
|
agents.bulkInvite({ emails: ['hello@hi.com'] });
|
||||||
|
expect(axiosMock.post).toHaveBeenCalledWith(
|
||||||
|
'/api/v1/agents/bulk_create',
|
||||||
|
{
|
||||||
|
emails: ['hello@hi.com'],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,20 +27,36 @@ describe('#NotificationAPI', () => {
|
|||||||
window.axios = originalAxios;
|
window.axios = originalAxios;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#get', () => {
|
describe('#get', () => {
|
||||||
notificationsAPI.get({
|
it('generates the API call if both params are available', () => {
|
||||||
page: 1,
|
notificationsAPI.get({
|
||||||
status: 'read',
|
|
||||||
type: 'Conversation',
|
|
||||||
sortOrder: 'desc',
|
|
||||||
});
|
|
||||||
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/notifications', {
|
|
||||||
params: {
|
|
||||||
page: 1,
|
page: 1,
|
||||||
status: 'read',
|
status: 'snoozed',
|
||||||
type: 'Conversation',
|
type: 'read',
|
||||||
sort_order: 'desc',
|
sortOrder: 'desc',
|
||||||
},
|
});
|
||||||
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/notifications', {
|
||||||
|
params: {
|
||||||
|
page: 1,
|
||||||
|
sort_order: 'desc',
|
||||||
|
includes: ['snoozed', 'read'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('generates the API call if one of the params are available', () => {
|
||||||
|
notificationsAPI.get({
|
||||||
|
page: 1,
|
||||||
|
type: 'read',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
});
|
||||||
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/notifications', {
|
||||||
|
params: {
|
||||||
|
page: 1,
|
||||||
|
sort_order: 'desc',
|
||||||
|
includes: ['read'],
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -111,6 +111,40 @@ describe('#Reports API', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('#getBotMetrics', () => {
|
||||||
|
reportsAPI.getBotMetrics({ from: 1621103400, to: 1621621800 });
|
||||||
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
|
'/api/v2/reports/bot_metrics',
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
since: 1621103400,
|
||||||
|
until: 1621621800,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#getBotSummary', () => {
|
||||||
|
reportsAPI.getBotSummary({
|
||||||
|
from: 1621103400,
|
||||||
|
to: 1621621800,
|
||||||
|
groupBy: 'date',
|
||||||
|
businessHours: true,
|
||||||
|
});
|
||||||
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
|
'/api/v2/reports/bot_summary',
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
since: 1621103400,
|
||||||
|
until: 1621621800,
|
||||||
|
type: 'account',
|
||||||
|
group_by: 'date',
|
||||||
|
business_hours: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('#getConversationMetric', () => {
|
it('#getConversationMetric', () => {
|
||||||
reportsAPI.getConversationMetric('account');
|
reportsAPI.getConversationMetric('account');
|
||||||
expect(axiosMock.get).toHaveBeenCalledWith(
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import SLAReportsAPI from '../slaReports';
|
||||||
|
import ApiClient from '../ApiClient';
|
||||||
|
|
||||||
|
describe('#SLAReports API', () => {
|
||||||
|
it('creates correct instance', () => {
|
||||||
|
expect(SLAReportsAPI).toBeInstanceOf(ApiClient);
|
||||||
|
expect(SLAReportsAPI.apiVersion).toBe('/api/v1');
|
||||||
|
expect(SLAReportsAPI).toHaveProperty('get');
|
||||||
|
expect(SLAReportsAPI).toHaveProperty('getMetrics');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('API calls', () => {
|
||||||
|
const originalAxios = window.axios;
|
||||||
|
const axiosMock = {
|
||||||
|
post: jest.fn(() => Promise.resolve()),
|
||||||
|
get: jest.fn(() => Promise.resolve()),
|
||||||
|
patch: jest.fn(() => Promise.resolve()),
|
||||||
|
delete: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.axios = axiosMock;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.axios = originalAxios;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#get', () => {
|
||||||
|
SLAReportsAPI.get({
|
||||||
|
page: 1,
|
||||||
|
from: 1622485800,
|
||||||
|
to: 1623695400,
|
||||||
|
assigned_agent_id: 1,
|
||||||
|
inbox_id: 1,
|
||||||
|
team_id: 1,
|
||||||
|
sla_policy_id: 1,
|
||||||
|
label_list: ['label1'],
|
||||||
|
});
|
||||||
|
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/applied_slas', {
|
||||||
|
params: {
|
||||||
|
page: 1,
|
||||||
|
since: 1622485800,
|
||||||
|
until: 1623695400,
|
||||||
|
assigned_agent_id: 1,
|
||||||
|
inbox_id: 1,
|
||||||
|
team_id: 1,
|
||||||
|
sla_policy_id: 1,
|
||||||
|
label_list: ['label1'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('#getMetrics', () => {
|
||||||
|
SLAReportsAPI.getMetrics({
|
||||||
|
from: 1622485800,
|
||||||
|
to: 1623695400,
|
||||||
|
assigned_agent_id: 1,
|
||||||
|
inbox_id: 1,
|
||||||
|
team_id: 1,
|
||||||
|
sla_policy_id: 1,
|
||||||
|
label_list: ['label1'],
|
||||||
|
});
|
||||||
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
|
'/api/v1/applied_slas/metrics',
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
since: 1622485800,
|
||||||
|
until: 1623695400,
|
||||||
|
assigned_agent_id: 1,
|
||||||
|
inbox_id: 1,
|
||||||
|
team_id: 1,
|
||||||
|
sla_policy_id: 1,
|
||||||
|
label_list: ['label1'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('#download', () => {
|
||||||
|
SLAReportsAPI.download({
|
||||||
|
from: 1622485800,
|
||||||
|
to: 1623695400,
|
||||||
|
assigned_agent_id: 1,
|
||||||
|
inbox_id: 1,
|
||||||
|
team_id: 1,
|
||||||
|
sla_policy_id: 1,
|
||||||
|
label_list: ['label1'],
|
||||||
|
});
|
||||||
|
expect(axiosMock.get).toHaveBeenCalledWith(
|
||||||
|
'/api/v1/applied_slas/download',
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
since: 1622485800,
|
||||||
|
until: 1623695400,
|
||||||
|
assigned_agent_id: 1,
|
||||||
|
inbox_id: 1,
|
||||||
|
team_id: 1,
|
||||||
|
sla_policy_id: 1,
|
||||||
|
label_list: ['label1'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
@import '~vue2-datepicker/scss/index';
|
@import '~vue2-datepicker/scss/index';
|
||||||
|
|
||||||
.date-picker {
|
.date-picker {
|
||||||
|
// To be removed one SLA reports date picker is created
|
||||||
|
&.small {
|
||||||
|
.mx-input {
|
||||||
|
@apply h-8 text-sm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.no-margin {
|
&.no-margin {
|
||||||
.mx-input {
|
.mx-input {
|
||||||
@apply mb-0;
|
@apply mb-0;
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
.button {
|
|
||||||
font-family: $body-font-family;
|
|
||||||
font-weight: $font-weight-medium;
|
|
||||||
|
|
||||||
&.round {
|
|
||||||
border-radius: 1000px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
height: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: var(--space-small);
|
|
||||||
padding: var(--space-normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
border: 0;
|
|
||||||
font-family: 'ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas',
|
|
||||||
'"Liberation Mono"', '"Courier New"', 'monospace';
|
|
||||||
font-size: $font-size-mini;
|
|
||||||
|
|
||||||
&.hljs {
|
|
||||||
background: $color-background;
|
|
||||||
border-radius: var(--border-radius-large);
|
|
||||||
padding: $space-two;
|
|
||||||
@apply bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-truncate {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-capitalize {
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cursor-pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove when grid gutters are fixed
|
|
||||||
.columns.with-right-space {
|
|
||||||
padding-right: var(--space-normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
border-radius: var(--border-radius-normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
.padding-right-small {
|
|
||||||
padding-right: var(--space-one);
|
|
||||||
}
|
|
||||||
@@ -1,623 +0,0 @@
|
|||||||
// Foundation for Sites Settings
|
|
||||||
// -----------------------------
|
|
||||||
//
|
|
||||||
// Table of Contents:
|
|
||||||
//
|
|
||||||
// 1. Global
|
|
||||||
// 2. Breakpoints
|
|
||||||
// 3. The Grid
|
|
||||||
// 4. Base Typography
|
|
||||||
// 5. Typography Helpers
|
|
||||||
// 6. Abide
|
|
||||||
// 7. Accordion
|
|
||||||
// 8. Accordion Menu
|
|
||||||
// 9. Badge
|
|
||||||
// 10. Breadcrumbs
|
|
||||||
// 11. Button
|
|
||||||
// 12. Button Group
|
|
||||||
// 13. Callout
|
|
||||||
// 14. Card
|
|
||||||
// 15. Close Button
|
|
||||||
// 16. Drilldown
|
|
||||||
// 17. Dropdown
|
|
||||||
// 18. Dropdown Menu
|
|
||||||
// 19. Forms
|
|
||||||
// 20. Label
|
|
||||||
// 21. Media Object
|
|
||||||
// 22. Menu
|
|
||||||
// 23. Meter
|
|
||||||
// 24. Off-canvas
|
|
||||||
// 25. Orbit
|
|
||||||
// 26. Pagination
|
|
||||||
// 27. Progress Bar
|
|
||||||
// 28. Responsive Embed
|
|
||||||
// 29. Reveal
|
|
||||||
// 30. Slider
|
|
||||||
// 31. Switch
|
|
||||||
// 32. Table
|
|
||||||
// 33. Tabs
|
|
||||||
// 34. Thumbnail
|
|
||||||
// 35. Title Bar
|
|
||||||
// 36. Tooltip
|
|
||||||
// 37. Top Bar
|
|
||||||
|
|
||||||
@import '~foundation-sites/scss/util/util';
|
|
||||||
// 1. Global
|
|
||||||
// ---------
|
|
||||||
|
|
||||||
// Disable contrast warnings in Foundation.
|
|
||||||
$contrast-warnings: false;
|
|
||||||
|
|
||||||
$global-font-size: 16px;
|
|
||||||
$global-width: 100%;
|
|
||||||
$global-lineheight: 1.5;
|
|
||||||
$foundation-palette: (primary: $color-woot,
|
|
||||||
secondary: #5d7592,
|
|
||||||
success: #44ce4b,
|
|
||||||
warning: #ffc532,
|
|
||||||
alert: #ff382d);
|
|
||||||
$light-gray: #c0ccda;
|
|
||||||
$medium-gray: #8492a6;
|
|
||||||
$dark-gray: $color-gray;
|
|
||||||
$black: #000;
|
|
||||||
$white: #fff;
|
|
||||||
$body-background: $white;
|
|
||||||
$body-font-color: $color-body;
|
|
||||||
$body-font-family: 'PlusJakarta',
|
|
||||||
-apple-system,
|
|
||||||
system-ui,
|
|
||||||
BlinkMacSystemFont,
|
|
||||||
"Segoe UI",
|
|
||||||
Roboto,
|
|
||||||
"Helvetica Neue",
|
|
||||||
Tahoma,
|
|
||||||
Arial,
|
|
||||||
sans-serif;
|
|
||||||
$body-antialiased: true;
|
|
||||||
$global-margin: $space-small;
|
|
||||||
$global-padding: $space-small;
|
|
||||||
$global-weight-normal: normal;
|
|
||||||
$global-weight-bold: bold;
|
|
||||||
$global-radius: 0;
|
|
||||||
$global-text-direction: ltr;
|
|
||||||
$global-flexbox: false;
|
|
||||||
$print-transparent-backgrounds: true;
|
|
||||||
|
|
||||||
@include add-foundation-colors;
|
|
||||||
|
|
||||||
// 2. Breakpoints
|
|
||||||
// --------------
|
|
||||||
|
|
||||||
$breakpoints: (small: 0,
|
|
||||||
medium: 640px,
|
|
||||||
large: 1024px,
|
|
||||||
xlarge: 1200px,
|
|
||||||
xxlarge: 1400px,
|
|
||||||
xxxlarge: 1600px,
|
|
||||||
);
|
|
||||||
$print-breakpoint: large;
|
|
||||||
$breakpoint-classes: (small medium large);
|
|
||||||
|
|
||||||
// 3. The Grid
|
|
||||||
// -----------
|
|
||||||
|
|
||||||
$grid-row-width: $global-width;
|
|
||||||
$grid-column-count: 12;
|
|
||||||
$grid-column-gutter: (small: $zero,
|
|
||||||
medium: $zero);
|
|
||||||
$grid-column-align-edge: true;
|
|
||||||
$block-grid-max: 8;
|
|
||||||
|
|
||||||
// 4. Base Typography
|
|
||||||
// ------------------
|
|
||||||
|
|
||||||
$header-font-family: $body-font-family;
|
|
||||||
$header-font-weight: $font-weight-medium;
|
|
||||||
$header-font-style: normal;
|
|
||||||
$font-family-monospace: $body-font-family;
|
|
||||||
$header-color: $color-heading;
|
|
||||||
$header-lineheight: 1.4;
|
|
||||||
$header-margin-bottom: 0.3125rem;
|
|
||||||
$header-styles: (small: ("h1": ("font-size": 24),
|
|
||||||
"h2": ("font-size": 20),
|
|
||||||
"h3": ("font-size": 19),
|
|
||||||
"h4": ("font-size": 18),
|
|
||||||
"h5": ("font-size": 17),
|
|
||||||
"h6": ("font-size": 16)),
|
|
||||||
medium: ("h1": ("font-size": 48),
|
|
||||||
"h2": ("font-size": 40),
|
|
||||||
"h3": ("font-size": 31),
|
|
||||||
"h4": ("font-size": 25),
|
|
||||||
"h5": ("font-size": 20),
|
|
||||||
"h6": ("font-size": 16)));
|
|
||||||
$header-text-rendering: optimizeLegibility;
|
|
||||||
$small-font-size: 80%;
|
|
||||||
$header-small-font-color: $medium-gray;
|
|
||||||
$paragraph-lineheight: 1.65;
|
|
||||||
$paragraph-margin-bottom: var(--space-small);
|
|
||||||
$paragraph-text-rendering: optimizeLegibility;
|
|
||||||
$code-color: $black;
|
|
||||||
$code-font-family: $font-family-monospace;
|
|
||||||
$code-font-weight: $global-weight-normal;
|
|
||||||
$code-background: $light-gray;
|
|
||||||
$code-border: 1px solid $medium-gray;
|
|
||||||
$code-padding: rem-calc(2 5 1);
|
|
||||||
$anchor-color: $primary-color;
|
|
||||||
$anchor-color-hover: scale-color($anchor-color, $lightness: -14%);
|
|
||||||
$anchor-text-decoration: none;
|
|
||||||
$anchor-text-decoration-hover: none;
|
|
||||||
$hr-width: $global-width;
|
|
||||||
$hr-border: 1px solid $medium-gray;
|
|
||||||
$hr-margin: rem-calc(20) auto;
|
|
||||||
$list-lineheight: $paragraph-lineheight;
|
|
||||||
$list-margin-bottom: $paragraph-margin-bottom;
|
|
||||||
$list-style-type: disc;
|
|
||||||
$list-style-position: outside;
|
|
||||||
$list-side-margin: 0.78125rem;
|
|
||||||
$list-nested-side-margin: 0.78125rem;
|
|
||||||
$defnlist-margin-bottom: 0.6875rem;
|
|
||||||
$defnlist-term-weight: $global-weight-bold;
|
|
||||||
$defnlist-term-margin-bottom: 0.1875rem;
|
|
||||||
$blockquote-color: $dark-gray;
|
|
||||||
$blockquote-padding: rem-calc(9 20 0 19);
|
|
||||||
$blockquote-border: 1px solid $medium-gray;
|
|
||||||
$cite-font-size: rem-calc(13);
|
|
||||||
$cite-color: $dark-gray;
|
|
||||||
$cite-pseudo-content: '\2014 \0020';
|
|
||||||
$keystroke-font: $font-family-monospace;
|
|
||||||
$keystroke-color: $black;
|
|
||||||
$keystroke-background: $light-gray;
|
|
||||||
$keystroke-padding: rem-calc(2 4 0);
|
|
||||||
$keystroke-radius: $global-radius;
|
|
||||||
$abbr-underline: 1px dotted $black;
|
|
||||||
|
|
||||||
// 5. Typography Helpers
|
|
||||||
// ---------------------
|
|
||||||
|
|
||||||
$lead-font-size: $global-font-size * 1.25;
|
|
||||||
$lead-lineheight: 1.6;
|
|
||||||
$subheader-lineheight: 1.4;
|
|
||||||
$subheader-color: $dark-gray;
|
|
||||||
$subheader-font-weight: $global-weight-normal;
|
|
||||||
$subheader-margin-top: 0.125rem;
|
|
||||||
$subheader-margin-bottom: 0.3125rem;
|
|
||||||
$stat-font-size: 1.5625rem;
|
|
||||||
|
|
||||||
// 6. Abide
|
|
||||||
// --------
|
|
||||||
|
|
||||||
$abide-inputs: true;
|
|
||||||
$abide-labels: true;
|
|
||||||
$input-background-invalid: get-color(alert);
|
|
||||||
$form-label-color-invalid: get-color(alert);
|
|
||||||
$input-error-color: get-color(alert);
|
|
||||||
$input-error-font-size: rem-calc(12);
|
|
||||||
$input-error-font-weight: $global-weight-bold;
|
|
||||||
|
|
||||||
// 7. Accordion
|
|
||||||
// ------------
|
|
||||||
|
|
||||||
$accordion-background: $white;
|
|
||||||
$accordion-plusminus: true;
|
|
||||||
$accordion-title-font-size: rem-calc(12);
|
|
||||||
$accordion-item-color: $primary-color;
|
|
||||||
$accordion-item-background-hover: $light-gray;
|
|
||||||
$accordion-item-padding: 0.78125rem 0.625rem;
|
|
||||||
$accordion-content-background: $white;
|
|
||||||
$accordion-content-border: 1px solid $light-gray;
|
|
||||||
$accordion-content-color: $body-font-color;
|
|
||||||
$accordion-content-padding: 0.625rem;
|
|
||||||
|
|
||||||
// 8. Accordion Menu
|
|
||||||
// -----------------
|
|
||||||
|
|
||||||
$accordionmenu-arrows: true;
|
|
||||||
$accordionmenu-arrow-color: $primary-color;
|
|
||||||
$accordionmenu-arrow-size: 6px;
|
|
||||||
|
|
||||||
// 9. Badge
|
|
||||||
// --------
|
|
||||||
|
|
||||||
$badge-background: $primary-color;
|
|
||||||
$badge-color: $white;
|
|
||||||
$badge-color-alt: $black;
|
|
||||||
$badge-palette: $foundation-palette;
|
|
||||||
$badge-padding: var(--space-smaller);
|
|
||||||
$badge-minwidth: 2.1em;
|
|
||||||
$badge-font-size: var(--font-size-nano);
|
|
||||||
|
|
||||||
// 10. Breadcrumbs
|
|
||||||
// ---------------
|
|
||||||
|
|
||||||
$breadcrumbs-margin: 0 0 $global-margin 0;
|
|
||||||
$breadcrumbs-item-font-size: rem-calc(11);
|
|
||||||
$breadcrumbs-item-color: $primary-color;
|
|
||||||
$breadcrumbs-item-color-current: $black;
|
|
||||||
$breadcrumbs-item-color-disabled: $medium-gray;
|
|
||||||
$breadcrumbs-item-margin: 0.46875rem;
|
|
||||||
$breadcrumbs-item-uppercase: true;
|
|
||||||
$breadcrumbs-item-slash: true;
|
|
||||||
|
|
||||||
// 11. Button
|
|
||||||
// ----------
|
|
||||||
|
|
||||||
$button-padding: var(--space-smaller) 1em;
|
|
||||||
$button-margin: 0 0 $global-margin 0;
|
|
||||||
$button-fill: solid;
|
|
||||||
$button-background: $primary-color;
|
|
||||||
$button-background-hover: scale-color($button-background, $lightness: -15%);
|
|
||||||
$button-color: $white;
|
|
||||||
$button-color-alt: $white;
|
|
||||||
$button-radius: var(--border-radius-normal);
|
|
||||||
$button-sizes: (tiny: var(--font-size-micro),
|
|
||||||
small: var(--font-size-mini),
|
|
||||||
default: var(--font-size-small),
|
|
||||||
large: var(--font-size-medium));
|
|
||||||
$button-palette: $foundation-palette;
|
|
||||||
$button-opacity-disabled: 0.4;
|
|
||||||
$button-background-hover-lightness: -20%;
|
|
||||||
$button-hollow-hover-lightness: -50%;
|
|
||||||
$button-transition: background-color 0.25s ease-out,
|
|
||||||
color 0.25s ease-out;
|
|
||||||
|
|
||||||
// 12. Button Group
|
|
||||||
// ----------------
|
|
||||||
|
|
||||||
$buttongroup-margin: 0;
|
|
||||||
$buttongroup-spacing: 0;
|
|
||||||
$buttongroup-child-selector: '.button';
|
|
||||||
$buttongroup-expand-max: 6;
|
|
||||||
$buttongroup-radius-on-each: false;
|
|
||||||
|
|
||||||
// 13. Callout
|
|
||||||
// -----------
|
|
||||||
|
|
||||||
$callout-background: $white;
|
|
||||||
$callout-background-fade: 85%;
|
|
||||||
$callout-border: 1px solid rgba($black, 0.25);
|
|
||||||
$callout-margin: 0 0 0.625rem 0;
|
|
||||||
$callout-padding: 0.625rem;
|
|
||||||
$callout-font-color: $body-font-color;
|
|
||||||
$callout-font-color-alt: $body-background;
|
|
||||||
$callout-radius: $global-radius;
|
|
||||||
$callout-link-tint: 30%;
|
|
||||||
|
|
||||||
// 14. Card
|
|
||||||
// --------
|
|
||||||
|
|
||||||
$card-background: $white;
|
|
||||||
$card-font-color: $body-font-color;
|
|
||||||
$card-divider-background: $light-gray;
|
|
||||||
$card-border: 1px solid var(--color-border);
|
|
||||||
$card-shadow: var(--shadow-small);
|
|
||||||
$card-border-radius: var(--border-radius-normal);
|
|
||||||
$card-padding: var(--space-small);
|
|
||||||
$card-margin: $global-margin;
|
|
||||||
|
|
||||||
// 15. Close Button
|
|
||||||
// ----------------
|
|
||||||
|
|
||||||
$closebutton-position: right top;
|
|
||||||
$closebutton-offset-horizontal: (small: 0.66rem,
|
|
||||||
medium: 1rem);
|
|
||||||
$closebutton-offset-vertical: (small: 0.33em,
|
|
||||||
medium: 0.5rem);
|
|
||||||
$closebutton-size: (small: 1.5em,
|
|
||||||
medium: 2em);
|
|
||||||
$closebutton-lineheight: 1;
|
|
||||||
$closebutton-color: $dark-gray;
|
|
||||||
$closebutton-color-hover: $black;
|
|
||||||
|
|
||||||
// 16. Drilldown
|
|
||||||
// -------------
|
|
||||||
|
|
||||||
$drilldown-transition: transform 0.15s linear;
|
|
||||||
$drilldown-arrows: true;
|
|
||||||
$drilldown-arrow-color: $primary-color;
|
|
||||||
$drilldown-arrow-size: 6px;
|
|
||||||
$drilldown-background: $white;
|
|
||||||
|
|
||||||
// 17. Dropdown
|
|
||||||
// ------------
|
|
||||||
|
|
||||||
$dropdown-padding: 0.625rem;
|
|
||||||
$dropdown-background: $body-background;
|
|
||||||
$dropdown-border: 1px solid $medium-gray;
|
|
||||||
$dropdown-font-size: 0.625rem;
|
|
||||||
$dropdown-width: 300px;
|
|
||||||
$dropdown-radius: $global-radius;
|
|
||||||
$dropdown-sizes: (tiny: 100px,
|
|
||||||
small: 200px,
|
|
||||||
large: 400px);
|
|
||||||
|
|
||||||
// 18. Dropdown Menu
|
|
||||||
// -----------------
|
|
||||||
|
|
||||||
$dropdownmenu-arrows: true;
|
|
||||||
$dropdownmenu-arrow-color: $anchor-color;
|
|
||||||
$dropdownmenu-arrow-size: 6px;
|
|
||||||
$dropdownmenu-min-width: 200px;
|
|
||||||
$dropdownmenu-background: $white;
|
|
||||||
$dropdownmenu-border: 1px solid $medium-gray;
|
|
||||||
|
|
||||||
// 19. Forms
|
|
||||||
// ---------
|
|
||||||
|
|
||||||
$fieldset-border: 1px solid $light-gray;
|
|
||||||
$fieldset-padding: $space-two;
|
|
||||||
$fieldset-margin: $space-one $zero;
|
|
||||||
$legend-padding: rem-calc(0 3);
|
|
||||||
$form-spacing: $space-normal;
|
|
||||||
$helptext-color: $color-body;
|
|
||||||
$helptext-font-size: $font-size-small;
|
|
||||||
$helptext-font-style: italic;
|
|
||||||
$input-prefix-color: $color-body;
|
|
||||||
$input-prefix-background: var(--b-100);
|
|
||||||
$input-prefix-border: 1px solid $color-border;
|
|
||||||
$input-prefix-padding: 0.625rem;
|
|
||||||
$form-label-color: $color-body;
|
|
||||||
$form-label-font-size: rem-calc(14);
|
|
||||||
$form-label-font-weight: $font-weight-medium;
|
|
||||||
$form-label-line-height: 1.8;
|
|
||||||
$select-background: $white;
|
|
||||||
$select-triangle-color: $dark-gray;
|
|
||||||
$select-radius: var(--border-radius-normal);
|
|
||||||
$input-color: $color-body;
|
|
||||||
$input-placeholder-color: $light-gray;
|
|
||||||
$input-font-family: inherit;
|
|
||||||
$input-font-size: $font-size-default;
|
|
||||||
$input-font-weight: $global-weight-normal;
|
|
||||||
$input-background: $white;
|
|
||||||
$input-background-focus: $white;
|
|
||||||
$input-background-disabled: $light-gray;
|
|
||||||
$input-border: 1px solid var(--s-200);
|
|
||||||
$input-border-focus: 1px solid lighten($primary-color, 15%);
|
|
||||||
$input-shadow: 0;
|
|
||||||
$input-shadow-focus: 0;
|
|
||||||
$input-cursor-disabled: not-allowed;
|
|
||||||
$input-transition: border-color 0.25s ease-in-out;
|
|
||||||
$input-number-spinners: true;
|
|
||||||
$input-radius: var(--border-radius-normal);
|
|
||||||
$form-button-radius: var(--border-radius-normal);
|
|
||||||
|
|
||||||
// 20. Label
|
|
||||||
// ---------
|
|
||||||
|
|
||||||
$label-background: $white;
|
|
||||||
$label-color: $black;
|
|
||||||
$label-color-alt: $black;
|
|
||||||
$label-palette: $foundation-palette;
|
|
||||||
$label-font-size: $font-size-mini;
|
|
||||||
$label-padding: $space-smaller $space-small;
|
|
||||||
$label-radius: var(--border-radius-small);
|
|
||||||
|
|
||||||
// 21. Media Object
|
|
||||||
// ----------------
|
|
||||||
|
|
||||||
$mediaobject-margin-bottom: $global-margin;
|
|
||||||
$mediaobject-section-padding: $global-padding;
|
|
||||||
$mediaobject-image-width-stacked: 100%;
|
|
||||||
|
|
||||||
// 22. Menu
|
|
||||||
// --------
|
|
||||||
|
|
||||||
$menu-margin: 0;
|
|
||||||
$menu-margin-nested: $space-medium;
|
|
||||||
$menu-item-padding: $space-slab;
|
|
||||||
$menu-item-color-active: $white;
|
|
||||||
$menu-item-background-active: $color-background;
|
|
||||||
$menu-icon-spacing: 0.15625rem;
|
|
||||||
$menu-item-background-hover: $light-gray;
|
|
||||||
$menu-border: $light-gray;
|
|
||||||
|
|
||||||
// 23. Meter
|
|
||||||
// ---------
|
|
||||||
|
|
||||||
$meter-height: 0.625rem;
|
|
||||||
$meter-radius: $global-radius;
|
|
||||||
$meter-background: $medium-gray;
|
|
||||||
$meter-fill-good: $success-color;
|
|
||||||
$meter-fill-medium: $warning-color;
|
|
||||||
$meter-fill-bad: $alert-color;
|
|
||||||
|
|
||||||
// 24. Off-canvas
|
|
||||||
// --------------
|
|
||||||
|
|
||||||
$offcanvas-sizes: (small: 14.375,
|
|
||||||
medium: 14.375,
|
|
||||||
);
|
|
||||||
$offcanvas-vertical-sizes: (small: 14.375,
|
|
||||||
medium: 14.375,
|
|
||||||
);
|
|
||||||
$offcanvas-background: $light-gray;
|
|
||||||
$offcanvas-shadow: 0 0 10px rgba($black, 0.7);
|
|
||||||
$offcanvas-push-zindex: 1;
|
|
||||||
$offcanvas-overlap-zindex: 10;
|
|
||||||
$offcanvas-reveal-zindex: 1;
|
|
||||||
$offcanvas-transition-length: 0.5s;
|
|
||||||
$offcanvas-transition-timing: ease;
|
|
||||||
$offcanvas-fixed-reveal: true;
|
|
||||||
$offcanvas-exit-background: rgba($white, 0.25);
|
|
||||||
$maincontent-class: 'off-canvas-content';
|
|
||||||
|
|
||||||
// 25. Orbit
|
|
||||||
// ---------
|
|
||||||
|
|
||||||
$orbit-bullet-background: $medium-gray;
|
|
||||||
$orbit-bullet-background-active: $dark-gray;
|
|
||||||
$orbit-bullet-diameter: 0.75rem;
|
|
||||||
$orbit-bullet-margin: 0.0625rem;
|
|
||||||
$orbit-bullet-margin-top: 0.5rem;
|
|
||||||
$orbit-bullet-margin-bottom: 0.5rem;
|
|
||||||
$orbit-caption-background: rgba($black, 0.5);
|
|
||||||
$orbit-caption-padding: 0.625rem;
|
|
||||||
$orbit-control-background-hover: rgba($black, 0.5);
|
|
||||||
$orbit-control-padding: 0.625rem;
|
|
||||||
$orbit-control-zindex: 10;
|
|
||||||
|
|
||||||
// 26. Pagination
|
|
||||||
// --------------
|
|
||||||
|
|
||||||
$pagination-font-size: rem-calc(14);
|
|
||||||
$pagination-margin-bottom: $global-margin;
|
|
||||||
$pagination-item-color: $black;
|
|
||||||
$pagination-item-padding: rem-calc(3 10);
|
|
||||||
$pagination-item-spacing: rem-calc(1);
|
|
||||||
$pagination-radius: $global-radius;
|
|
||||||
$pagination-item-background-hover: $light-gray;
|
|
||||||
$pagination-item-background-current: $primary-color;
|
|
||||||
$pagination-item-color-current: $white;
|
|
||||||
$pagination-item-color-disabled: $medium-gray;
|
|
||||||
$pagination-ellipsis-color: $black;
|
|
||||||
$pagination-mobile-items: false;
|
|
||||||
$pagination-mobile-current-item: false;
|
|
||||||
$pagination-arrows: true;
|
|
||||||
|
|
||||||
// 27. Progress Bar
|
|
||||||
// ----------------
|
|
||||||
|
|
||||||
$progress-height: 0.625rem;
|
|
||||||
$progress-background: $medium-gray;
|
|
||||||
$progress-margin-bottom: $global-margin;
|
|
||||||
$progress-meter-background: $primary-color;
|
|
||||||
$progress-radius: $global-radius;
|
|
||||||
|
|
||||||
// 28. Responsive Embed
|
|
||||||
// --------------------
|
|
||||||
|
|
||||||
$responsive-embed-margin-bottom: rem-calc(16);
|
|
||||||
$responsive-embed-ratios: (default: 4 by 3,
|
|
||||||
widescreen: 16 by 9);
|
|
||||||
|
|
||||||
// 29. Reveal
|
|
||||||
// ----------
|
|
||||||
|
|
||||||
$reveal-background: $white;
|
|
||||||
$reveal-width: 600px;
|
|
||||||
$reveal-max-width: $global-width;
|
|
||||||
$reveal-padding: $global-padding;
|
|
||||||
$reveal-border: 1px solid $medium-gray;
|
|
||||||
$reveal-radius: $global-radius;
|
|
||||||
$reveal-zindex: 1005;
|
|
||||||
$reveal-overlay-background: rgba($black, 0.45);
|
|
||||||
|
|
||||||
// 30. Slider
|
|
||||||
// ----------
|
|
||||||
|
|
||||||
$slider-width-vertical: 0.3125rem;
|
|
||||||
$slider-transition: all 0.2s ease-in-out;
|
|
||||||
$slider-height: 0.3125rem;
|
|
||||||
$slider-background: $light-gray;
|
|
||||||
$slider-fill-background: $medium-gray;
|
|
||||||
$slider-handle-height: 0.875rem;
|
|
||||||
$slider-handle-width: 0.875rem;
|
|
||||||
$slider-handle-background: $primary-color;
|
|
||||||
$slider-opacity-disabled: 0.25;
|
|
||||||
$slider-radius: $global-radius;
|
|
||||||
|
|
||||||
// 31. Switch
|
|
||||||
// ----------
|
|
||||||
|
|
||||||
$switch-background: $light-gray;
|
|
||||||
$switch-background-active: $primary-color;
|
|
||||||
$switch-height: $space-two;
|
|
||||||
$switch-height-tiny: $space-slab;
|
|
||||||
$switch-height-small: $space-normal;
|
|
||||||
$switch-height-large: $space-large;
|
|
||||||
$switch-radius: $space-large;
|
|
||||||
$switch-margin: $global-margin;
|
|
||||||
$switch-paddle-background: $white;
|
|
||||||
$switch-paddle-offset: $space-micro;
|
|
||||||
$switch-paddle-radius: $space-large;
|
|
||||||
$switch-paddle-transition: all 0.15s ease-out;
|
|
||||||
|
|
||||||
// 32. Table
|
|
||||||
// ---------
|
|
||||||
|
|
||||||
$table-background: transparent;
|
|
||||||
$table-color-scale: 5%;
|
|
||||||
$table-border: 1px solid transparent;
|
|
||||||
$table-padding: rem-calc(8 10 10);
|
|
||||||
$table-hover-scale: 2%;
|
|
||||||
$table-row-hover: darken($table-background, $table-hover-scale);
|
|
||||||
$table-row-stripe-hover: darken($table-background,
|
|
||||||
$table-color-scale + $table-hover-scale);
|
|
||||||
$table-is-striped: false;
|
|
||||||
$table-striped-background: smart-scale($table-background, $table-color-scale);
|
|
||||||
$table-stripe: even;
|
|
||||||
$table-head-background: smart-scale($table-background, $table-color-scale / 2);
|
|
||||||
$table-head-row-hover: darken($table-head-background, $table-hover-scale);
|
|
||||||
$table-foot-background: smart-scale($table-background, $table-color-scale);
|
|
||||||
$table-foot-row-hover: darken($table-foot-background, $table-hover-scale);
|
|
||||||
$table-head-font-color: $body-font-color;
|
|
||||||
$table-foot-font-color: $body-font-color;
|
|
||||||
$show-header-for-stacked: false;
|
|
||||||
|
|
||||||
// 33. Tabs
|
|
||||||
// --------
|
|
||||||
|
|
||||||
$tab-margin: 0;
|
|
||||||
|
|
||||||
$tab-background: transparent;
|
|
||||||
$tab-background-active: transparent;
|
|
||||||
$tab-item-font-size: $font-size-small;
|
|
||||||
$tab-item-background-hover: transparent;
|
|
||||||
$tab-item-padding: $space-one $zero;
|
|
||||||
$tab-color: $primary-color;
|
|
||||||
$tab-active-color: $primary-color;
|
|
||||||
$tab-expand-max: 6;
|
|
||||||
$tab-content-background: transparent;
|
|
||||||
$tab-content-border: transparent;
|
|
||||||
$tab-content-color: foreground($tab-background, $primary-color);
|
|
||||||
$tab-content-padding: 0.625rem;
|
|
||||||
|
|
||||||
// 34. Thumbnail
|
|
||||||
// -------------
|
|
||||||
|
|
||||||
$thumbnail-border: solid 4px $white;
|
|
||||||
$thumbnail-margin-bottom: $global-margin;
|
|
||||||
$thumbnail-shadow: 0 0 0 1px rgba($black, 0.2);
|
|
||||||
$thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5);
|
|
||||||
$thumbnail-transition: box-shadow 200ms ease-out;
|
|
||||||
$thumbnail-radius: $global-radius;
|
|
||||||
|
|
||||||
// 35. Title Bar
|
|
||||||
// -------------
|
|
||||||
|
|
||||||
$titlebar-background: $black;
|
|
||||||
$titlebar-color: $white;
|
|
||||||
$titlebar-padding: 0.3125rem;
|
|
||||||
$titlebar-text-font-weight: bold;
|
|
||||||
$titlebar-icon-color: $white;
|
|
||||||
$titlebar-icon-color-hover: $medium-gray;
|
|
||||||
$titlebar-icon-spacing: 0.15625rem;
|
|
||||||
|
|
||||||
// 36. Tooltip
|
|
||||||
// -----------
|
|
||||||
|
|
||||||
$has-tip-font-weight: $global-weight-bold;
|
|
||||||
$has-tip-border-bottom: dotted 1px $dark-gray;
|
|
||||||
$tooltip-background-color: $black;
|
|
||||||
$tooltip-color: $white;
|
|
||||||
$tooltip-padding: 0.46875rem;
|
|
||||||
$tooltip-font-size: $font-size-mini;
|
|
||||||
$tooltip-pip-width: 0.46875rem;
|
|
||||||
$tooltip-pip-height: $tooltip-pip-width * 0.866;
|
|
||||||
$tooltip-radius: $global-radius;
|
|
||||||
|
|
||||||
// 37. Top Bar
|
|
||||||
// -----------
|
|
||||||
|
|
||||||
$topbar-padding: 0.3125;
|
|
||||||
$topbar-background: $light-gray;
|
|
||||||
$topbar-submenu-background: $topbar-background;
|
|
||||||
$topbar-title-spacing: 0.3125 0.625rem 0.3125 0;
|
|
||||||
$topbar-input-width: 200px;
|
|
||||||
$topbar-unstack-breakpoint: medium;
|
|
||||||
|
|
||||||
|
|
||||||
// Internal variable that contains the flex justifying options
|
|
||||||
$-zf-flex-justify: -zf-flex-justify($global-text-direction);
|
|
||||||
|
|
||||||
$menu-items-padding: $space-one;
|
|
||||||
$xy-grid: false;
|
|
||||||
@@ -1,64 +1,22 @@
|
|||||||
.bg-light {
|
// loader class
|
||||||
@apply bg-slate-25 dark:bg-slate-800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-center {
|
|
||||||
@include flex-align(center, middle);
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-space-fix {
|
|
||||||
margin-bottom: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-height {
|
|
||||||
@include full-height();
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
@include color-spinner();
|
@include color-spinner();
|
||||||
display: inline-block;
|
@apply inline-block h-6 py-0 px-6 relative align-middle w-6;
|
||||||
height: $space-medium;
|
|
||||||
padding: $zero $space-medium;
|
|
||||||
position: relative;
|
|
||||||
vertical-align: middle;
|
|
||||||
width: $space-medium;
|
|
||||||
|
|
||||||
&.message {
|
&.message {
|
||||||
@include normal-shadow;
|
@include normal-shadow;
|
||||||
background: $color-white;
|
@apply bg-white dark:bg-slate-800 rounded-full left-0 my-3 mx-auto p-4 top-0;
|
||||||
border-radius: $space-large;
|
|
||||||
left: 0;
|
|
||||||
margin: $space-slab auto;
|
|
||||||
padding: $space-normal;
|
|
||||||
top: 0;
|
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
margin-left: -$space-slab;
|
@apply -ml-3 -mt-3;
|
||||||
margin-top: -$space-slab;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.small {
|
&.small {
|
||||||
height: $space-normal;
|
@apply h-4 w-4;
|
||||||
width: $space-normal;
|
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
height: $space-normal;
|
@apply h-4 -mt-2 w-4;
|
||||||
margin-top: -$space-small;
|
|
||||||
width: $space-normal;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.justify-space-between {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w-full {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-full {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
|
// scss-lint:disable SpaceAfterPropertyColon
|
||||||
|
@import 'shared/assets/fonts/inter';
|
||||||
|
// Inter,
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
|
font-family:
|
||||||
|
'PlusJakarta',
|
||||||
|
-apple-system,
|
||||||
|
system-ui,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
'Segoe UI',
|
||||||
|
Roboto,
|
||||||
|
'Helvetica Neue',
|
||||||
|
Tahoma,
|
||||||
|
Arial,
|
||||||
|
sans-serif !important;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -131,37 +131,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-header--wrap {
|
|
||||||
.search--input {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-switch__container {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basic filter dropdown
|
// Basic filter dropdown
|
||||||
.basic-filter {
|
.basic-filter {
|
||||||
left: 0;
|
left: 0;
|
||||||
right: unset;
|
right: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 actions
|
||||||
.bulk-action__container {
|
.bulk-action__container {
|
||||||
.triangle {
|
.triangle {
|
||||||
@@ -202,22 +177,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// Help center
|
||||||
.article-container .row--article-block {
|
.article-container .row--article-block {
|
||||||
td:last-child {
|
td:last-child {
|
||||||
@@ -324,10 +283,6 @@
|
|||||||
|
|
||||||
// Other changes
|
// Other changes
|
||||||
|
|
||||||
.account-selector--wrap {
|
|
||||||
direction: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.colorpicker--chrome {
|
.colorpicker--chrome {
|
||||||
direction: initial;
|
direction: initial;
|
||||||
}
|
}
|
||||||
@@ -347,9 +302,4 @@
|
|||||||
.contact--form .input-group {
|
.contact--form .input-group {
|
||||||
direction: initial;
|
direction: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
// scss-lint:disable QualifyingElement
|
|
||||||
.dropdown-menu--header > span.title {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
.page-title {
|
|
||||||
font-size: $font-size-big;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-sub-title {
|
|
||||||
font-size: $font-size-large;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.block-title {
|
|
||||||
font-size: $font-size-medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub-block-title {
|
|
||||||
font-size: $font-size-default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-block-title {
|
|
||||||
font-size: $font-size-small;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-muted {
|
|
||||||
color: var(--s-300);
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-size: $font-size-small;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: $font-size-small;
|
|
||||||
word-spacing: .12em;
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
.margin-bottom-small {
|
|
||||||
margin-bottom: var(--space-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.margin-right-smaller {
|
|
||||||
margin-right: var(--space-smaller);
|
|
||||||
}
|
|
||||||
|
|
||||||
.margin-left-minus-slab {
|
|
||||||
margin-left: var(--space-minus-slab);
|
|
||||||
}
|
|
||||||
|
|
||||||
.margin-right-minus-slab {
|
|
||||||
margin-right: var(--space-minus-slab);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fs-small {
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fs-default {
|
|
||||||
font-size: var(--font-size-default);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fw-medium {
|
|
||||||
font-weight: var(--font-weight-medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-normal {
|
|
||||||
padding: var(--space-normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
.overflow-scroll {
|
|
||||||
overflow: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overflow-auto {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overflow-hidden {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.border-right {
|
|
||||||
@apply border-r border-slate-50 dark:border-slate-700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.border-left {
|
|
||||||
border-left: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-ellipsis {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-between {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-end {
|
|
||||||
display: flex;
|
|
||||||
justify-content: end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-align-center {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
@@ -15,63 +15,372 @@
|
|||||||
@import 'variables';
|
@import 'variables';
|
||||||
|
|
||||||
@import 'mixins';
|
@import 'mixins';
|
||||||
@import 'foundation-settings';
|
|
||||||
@import 'helper-classes';
|
@import 'helper-classes';
|
||||||
@import 'formulate';
|
@import 'formulate';
|
||||||
@import 'date-picker';
|
@import 'date-picker';
|
||||||
|
|
||||||
@import 'foundation-sites/scss/foundation';
|
|
||||||
|
|
||||||
@include foundation-everything($flex: true);
|
|
||||||
|
|
||||||
@include foundation-prototype-text-utilities;
|
|
||||||
@include foundation-prototype-text-transformation;
|
|
||||||
@include foundation-prototype-text-decoration;
|
|
||||||
@include foundation-prototype-font-styling;
|
|
||||||
@include foundation-prototype-list-style-type;
|
|
||||||
@include foundation-prototype-rounded;
|
|
||||||
@include foundation-prototype-bordered;
|
|
||||||
@include foundation-prototype-shadow;
|
|
||||||
@include foundation-prototype-separator;
|
|
||||||
@include foundation-prototype-overflow;
|
|
||||||
@include foundation-prototype-display;
|
|
||||||
@include foundation-prototype-position;
|
|
||||||
@include foundation-prototype-border-box;
|
|
||||||
@include foundation-prototype-border-none;
|
|
||||||
@include foundation-prototype-sizing;
|
|
||||||
@include foundation-prototype-spacing;
|
|
||||||
|
|
||||||
@import 'typography';
|
|
||||||
@import 'layout';
|
@import 'layout';
|
||||||
@import 'animations';
|
@import 'animations';
|
||||||
@import 'foundation-custom';
|
|
||||||
@import 'rtl';
|
@import 'rtl';
|
||||||
|
|
||||||
|
@import 'widgets/base';
|
||||||
@import 'widgets/buttons';
|
@import 'widgets/buttons';
|
||||||
@import 'widgets/conv-header';
|
|
||||||
@import 'widgets/conversation-card';
|
|
||||||
@import 'widgets/conversation-view';
|
@import 'widgets/conversation-view';
|
||||||
@import 'widgets/forms';
|
|
||||||
@import 'widgets/login';
|
|
||||||
@import 'widgets/modal';
|
|
||||||
@import 'widgets/reply-box';
|
|
||||||
@import 'widgets/report';
|
|
||||||
@import 'widgets/search-box';
|
|
||||||
@import 'widgets/sidemenu';
|
|
||||||
@import 'widgets/snackbar';
|
|
||||||
@import 'widgets/states';
|
|
||||||
@import 'widgets/status-bar';
|
|
||||||
@import 'widgets/tabs';
|
@import 'widgets/tabs';
|
||||||
@import 'widgets/woot-tables';
|
@import 'widgets/woot-tables';
|
||||||
|
|
||||||
@import 'views/settings/inbox';
|
|
||||||
@import 'views/settings/integrations';
|
|
||||||
|
|
||||||
@import 'plugins/multiselect';
|
@import 'plugins/multiselect';
|
||||||
@import 'plugins/dropdown';
|
@import 'plugins/dropdown';
|
||||||
@import '~shared/assets/stylesheets/ionicons';
|
@import '~shared/assets/stylesheets/ionicons';
|
||||||
@import 'utility-helpers';
|
|
||||||
|
|
||||||
.tooltip {
|
.tooltip {
|
||||||
@apply bg-slate-900 text-white py-1 px-2 z-40 text-xs rounded-md dark:bg-slate-200 dark:text-slate-900;
|
@apply bg-slate-900 text-white py-1 px-2 z-40 text-xs rounded-md dark:bg-slate-200 dark:text-slate-900;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hide {
|
||||||
|
@apply hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
// scss-lint:disable PropertySortOrder
|
||||||
|
:root {
|
||||||
|
--color-amber-25: 254 253 251;
|
||||||
|
--color-amber-50: 255 249 237;
|
||||||
|
--color-amber-75: 255 243 208;
|
||||||
|
--color-amber-100: 255 236 183;
|
||||||
|
--color-amber-200: 255 224 161;
|
||||||
|
--color-amber-300: 245 208 140;
|
||||||
|
--color-amber-400: 228 187 120;
|
||||||
|
--color-amber-500: 214 163 92;
|
||||||
|
--color-amber-600: 214 163 92;
|
||||||
|
--color-amber-700: 255 186 26;
|
||||||
|
--color-amber-800: 145 89 48;
|
||||||
|
--color-amber-900: 79 52 34;
|
||||||
|
|
||||||
|
--color-ash-100: 235 235 239;
|
||||||
|
--color-ash-200: 228 228 233;
|
||||||
|
--color-ash-25: 252 252 253;
|
||||||
|
--color-ash-300: 221 221 227;
|
||||||
|
--color-ash-400: 211 212 219;
|
||||||
|
--color-ash-50: 249 249 251;
|
||||||
|
--color-ash-500: 185 187 198;
|
||||||
|
--color-ash-600: 139 141 152;
|
||||||
|
--color-ash-700: 126 128 138;
|
||||||
|
--color-ash-75: 242 242 245;
|
||||||
|
--color-ash-800: 96 100 108;
|
||||||
|
--color-ash-900: 28 32 36;
|
||||||
|
|
||||||
|
--color-primary-25: 251 253 255;
|
||||||
|
--color-primary-50: 245 249 255;
|
||||||
|
--color-primary-75: 233 243 255;
|
||||||
|
--color-primary-100: 218 236 255;
|
||||||
|
--color-primary-200: 201 226 255;
|
||||||
|
--color-primary-300: 181 213 255;
|
||||||
|
--color-primary-400: 155 195 252;
|
||||||
|
--color-primary-500: 117 171 247;
|
||||||
|
--color-primary-600: 39 129 246;
|
||||||
|
--color-primary-700: 16 115 233;
|
||||||
|
--color-primary-800: 8 109 224;
|
||||||
|
--color-primary-900: 11 50 101;
|
||||||
|
|
||||||
|
--color-ruby-100: 255 220 225;
|
||||||
|
--color-ruby-200: 255 206 214;
|
||||||
|
--color-ruby-25: 255 252 253;
|
||||||
|
--color-ruby-300: 248 191 200;
|
||||||
|
--color-ruby-400: 239 172 184;
|
||||||
|
--color-ruby-50: 255 247 248;
|
||||||
|
--color-ruby-500: 229 146 163;
|
||||||
|
--color-ruby-600: 229 70 102;
|
||||||
|
--color-ruby-700: 220 59 93;
|
||||||
|
--color-ruby-75: 254 234 237;
|
||||||
|
--color-ruby-800: 202 36 77;
|
||||||
|
--color-ruby-900: 100 23 43;
|
||||||
|
|
||||||
|
--color-teal-100: 224 248 243;
|
||||||
|
--color-teal-200: 204 243 234;
|
||||||
|
--color-teal-25: 250 254 253;
|
||||||
|
--color-teal-300: 184 234 224;
|
||||||
|
--color-teal-400: 161 222 210;
|
||||||
|
--color-teal-50: 243 251 249;
|
||||||
|
--color-teal-500: 83 185 171;
|
||||||
|
--color-teal-600: 18 165 148;
|
||||||
|
--color-teal-700: 13 155 138;
|
||||||
|
--color-teal-75: 236 249 255;
|
||||||
|
--color-teal-800: 0 133 115;
|
||||||
|
--color-teal-900: 13 61 56;
|
||||||
|
|
||||||
|
--color-green-25: 251 254 252;
|
||||||
|
--color-green-50: 244 251 246;
|
||||||
|
--color-green-75: 230 246 235;
|
||||||
|
--color-green-100: 214 241 223;
|
||||||
|
--color-green-200: 196 232 209;
|
||||||
|
--color-green-300: 173 221 192;
|
||||||
|
--color-green-400: 142 206 170;
|
||||||
|
--color-green-500: 91 185 139;
|
||||||
|
--color-green-600: 48 164 108;
|
||||||
|
--color-green-700: 43 154 102;
|
||||||
|
--color-green-800: 33 131 88;
|
||||||
|
--color-green-900: 25 59 45;
|
||||||
|
|
||||||
|
--color-mint-25: 249 254 253;
|
||||||
|
--color-mint-50: 242 251 249;
|
||||||
|
--color-mint-75: 221 249 242;
|
||||||
|
--color-mint-100: 200 244 233;
|
||||||
|
--color-mint-200: 179 236 222;
|
||||||
|
--color-mint-300: 156 224 208;
|
||||||
|
--color-mint-400: 126 207 189;
|
||||||
|
--color-mint-500: 76 187 165;
|
||||||
|
--color-mint-600: 134 234 212;
|
||||||
|
--color-mint-700: 125 224 203;
|
||||||
|
--color-mint-800: 2 120 100;
|
||||||
|
--color-mint-900: 22 67 60;
|
||||||
|
|
||||||
|
--color-sky-25: 249 254 255;
|
||||||
|
--color-sky-50: 241 250 253;
|
||||||
|
--color-sky-75: 225 246 253;
|
||||||
|
--color-sky-100: 209 240 250;
|
||||||
|
--color-sky-200: 190 231 245;
|
||||||
|
--color-sky-300: 169 218 237;
|
||||||
|
--color-sky-400: 141 202 227;
|
||||||
|
--color-sky-500: 96 179 215;
|
||||||
|
--color-sky-600: 124 226 254;
|
||||||
|
--color-sky-700: 116 218 248;
|
||||||
|
--color-sky-800: 0 116 158;
|
||||||
|
--color-sky-900: 29 62 86;
|
||||||
|
|
||||||
|
--color-indigo-25: 253 253 254;
|
||||||
|
--color-indigo-50: 247 249 255;
|
||||||
|
--color-indigo-75: 237 242 254;
|
||||||
|
--color-indigo-100: 225 233 255;
|
||||||
|
--color-indigo-200: 210 222 255;
|
||||||
|
--color-indigo-300: 193 208 255;
|
||||||
|
--color-indigo-400: 171 189 249;
|
||||||
|
--color-indigo-500: 141 164 239;
|
||||||
|
--color-indigo-600: 62 99 221;
|
||||||
|
--color-indigo-700: 51 88 212;
|
||||||
|
--color-indigo-800: 58 91 199;
|
||||||
|
--color-indigo-900: 31 45 92;
|
||||||
|
|
||||||
|
--color-iris-25: 253 253 255;
|
||||||
|
--color-iris-50: 248 248 255;
|
||||||
|
--color-iris-75: 240 241 254;
|
||||||
|
--color-iris-100: 230 231 255;
|
||||||
|
--color-iris-200: 218 220 255;
|
||||||
|
--color-iris-300: 203 205 255;
|
||||||
|
--color-iris-400: 184 186 248;
|
||||||
|
--color-iris-500: 155 158 240;
|
||||||
|
--color-iris-600: 91 91 214;
|
||||||
|
--color-iris-700: 81 81 205;
|
||||||
|
--color-iris-800: 87 83 198;
|
||||||
|
--color-iris-900: 39 41 98;
|
||||||
|
|
||||||
|
--color-violet-25: 253 252 254;
|
||||||
|
--color-violet-50: 250 248 255;
|
||||||
|
--color-violet-75: 244 240 254;
|
||||||
|
--color-violet-100: 235 228 255;
|
||||||
|
--color-violet-200: 225 217 255;
|
||||||
|
--color-violet-300: 212 202 254;
|
||||||
|
--color-violet-400: 194 181 245;
|
||||||
|
--color-violet-500: 170 153 236;
|
||||||
|
--color-violet-600: 110 86 207;
|
||||||
|
--color-violet-700: 101 77 196;
|
||||||
|
--color-violet-800: 101 80 185;
|
||||||
|
--color-violet-900: 47 38 95;
|
||||||
|
|
||||||
|
--color-pink-25: 255 252 254;
|
||||||
|
--color-pink-50: 254 247 251;
|
||||||
|
--color-pink-75: 254 233 245;
|
||||||
|
--color-pink-100: 251 220 239;
|
||||||
|
--color-pink-200: 246 206 231;
|
||||||
|
--color-pink-300: 239 191 221;
|
||||||
|
--color-pink-400: 231 172 208;
|
||||||
|
--color-pink-500: 221 147 194;
|
||||||
|
--color-pink-600: 214 64 159;
|
||||||
|
--color-pink-700: 207 56 151;
|
||||||
|
--color-pink-800: 194 41 138;
|
||||||
|
--color-pink-900: 101 18 73;
|
||||||
|
|
||||||
|
--color-orange-25: 254 252 251;
|
||||||
|
--color-orange-50: 255 247 237;
|
||||||
|
--color-orange-75: 255 239 214;
|
||||||
|
--color-orange-100: 255 223 181;
|
||||||
|
--color-orange-200: 255 209 154;
|
||||||
|
--color-orange-300: 255 193 130;
|
||||||
|
--color-orange-400: 245 174 115;
|
||||||
|
--color-orange-500: 236 148 85;
|
||||||
|
--color-orange-600: 247 107 21;
|
||||||
|
--color-orange-700: 239 95 0;
|
||||||
|
--color-orange-800: 204 78 0;
|
||||||
|
--color-orange-900: 88 45 29;
|
||||||
|
}
|
||||||
|
// scss-lint:disable QualifyingElement
|
||||||
|
body.dark {
|
||||||
|
--color-amber-25: 31 19 0;
|
||||||
|
--color-amber-50: 37 24 4;
|
||||||
|
--color-amber-75: 48 32 11;
|
||||||
|
--color-amber-100: 57 39 15;
|
||||||
|
--color-amber-200: 67 46 18;
|
||||||
|
--color-amber-300: 83 57 22;
|
||||||
|
--color-amber-400: 111 77 29;
|
||||||
|
--color-amber-500: 169 118 42;
|
||||||
|
--color-amber-600: 169 118 42;
|
||||||
|
--color-amber-700: 255 203 71;
|
||||||
|
--color-amber-800: 255 204 77;
|
||||||
|
--color-amber-900: 255 231 179;
|
||||||
|
|
||||||
|
--color-ash-100: 46 48 53;
|
||||||
|
--color-ash-200: 53 55 60;
|
||||||
|
--color-ash-25: 24 24 26;
|
||||||
|
--color-ash-300: 60 63 68;
|
||||||
|
--color-ash-400: 70 75 80;
|
||||||
|
--color-ash-50: 27 27 31;
|
||||||
|
--color-ash-500: 90 97 101;
|
||||||
|
--color-ash-600: 105 110 119;
|
||||||
|
--color-ash-700: 120 127 133;
|
||||||
|
--color-ash-75: 39 40 45;
|
||||||
|
--color-ash-800: 173 177 184;
|
||||||
|
--color-ash-900: 237 238 240;
|
||||||
|
|
||||||
|
--color-primary-25: 10 17 28;
|
||||||
|
--color-primary-50: 15 24 38;
|
||||||
|
--color-primary-75: 15 39 72;
|
||||||
|
--color-primary-100: 10 49 99;
|
||||||
|
--color-primary-200: 18 61 117;
|
||||||
|
--color-primary-300: 29 74 134;
|
||||||
|
--color-primary-400: 40 89 156;
|
||||||
|
--color-primary-500: 48 106 186;
|
||||||
|
--color-primary-600: 39 129 246;
|
||||||
|
--color-primary-700: 21 116 231;
|
||||||
|
--color-primary-800: 126 182 255;
|
||||||
|
--color-primary-900: 205 227 255;
|
||||||
|
|
||||||
|
--color-ruby-100: 78 19 37;
|
||||||
|
--color-ruby-200: 94 26 46;
|
||||||
|
--color-ruby-25: 25 17 19;
|
||||||
|
--color-ruby-300: 111 37 57;
|
||||||
|
--color-ruby-400: 136 52 71;
|
||||||
|
--color-ruby-50: 30 21 23;
|
||||||
|
--color-ruby-500: 179 68 90;
|
||||||
|
--color-ruby-600: 229 70 102;
|
||||||
|
--color-ruby-700: 236 90 114;
|
||||||
|
--color-ruby-75: 58 20 30;
|
||||||
|
--color-ruby-800: 255 148 157;
|
||||||
|
--color-ruby-900: 254 210 225;
|
||||||
|
|
||||||
|
--color-teal-100: 2 59 55;
|
||||||
|
--color-teal-200: 8 72 67;
|
||||||
|
--color-teal-25: 13 21 20;
|
||||||
|
--color-teal-300: 28 105 97;
|
||||||
|
--color-teal-400: 28 105 97;
|
||||||
|
--color-teal-50: 17 28 27;
|
||||||
|
--color-teal-500: 32 126 115;
|
||||||
|
--color-teal-600: 41 163 131;
|
||||||
|
--color-teal-700: 14 179 158;
|
||||||
|
--color-teal-75: 13 45 42;
|
||||||
|
--color-teal-800: 11 216 182;
|
||||||
|
--color-teal-900: 173 240 221;
|
||||||
|
|
||||||
|
--color-green-25: 14 21 18;
|
||||||
|
--color-green-50: 18 27 23;
|
||||||
|
--color-green-75: 19 45 33;
|
||||||
|
--color-green-100: 17 59 41;
|
||||||
|
--color-green-200: 23 73 51;
|
||||||
|
--color-green-300: 32 87 62;
|
||||||
|
--color-green-400: 40 104 74;
|
||||||
|
--color-green-500: 47 124 87;
|
||||||
|
--color-green-600: 48 164 108;
|
||||||
|
--color-green-700: 51 176 116;
|
||||||
|
--color-green-800: 61 214 140;
|
||||||
|
--color-green-900: 177 241 203;
|
||||||
|
|
||||||
|
--color-mint-25: 14 21 21;
|
||||||
|
--color-mint-50: 15 27 27;
|
||||||
|
--color-mint-75: 9 44 43;
|
||||||
|
--color-mint-100: 0 58 56;
|
||||||
|
--color-mint-200: 0 71 68;
|
||||||
|
--color-mint-300: 16 86 80;
|
||||||
|
--color-mint-400: 30 104 95;
|
||||||
|
--color-mint-500: 39 127 112;
|
||||||
|
--color-mint-600: 134 234 212;
|
||||||
|
--color-mint-700: 168 245 229;
|
||||||
|
--color-mint-800: 88 213 186;
|
||||||
|
--color-mint-900: 196 245 225;
|
||||||
|
|
||||||
|
--color-sky-25: 14 21 21;
|
||||||
|
--color-sky-50: 15 27 27;
|
||||||
|
--color-sky-75: 9 44 43;
|
||||||
|
--color-sky-100: 0 58 56;
|
||||||
|
--color-sky-200: 0 71 68;
|
||||||
|
--color-sky-300: 16 86 80;
|
||||||
|
--color-sky-400: 30 104 95;
|
||||||
|
--color-sky-500: 39 127 112;
|
||||||
|
--color-sky-600: 134 234 212;
|
||||||
|
--color-sky-700: 168 245 229;
|
||||||
|
--color-sky-800: 88 213 186;
|
||||||
|
--color-sky-900: 196 245 225;
|
||||||
|
|
||||||
|
--color-indigo-25: 17 19 31;
|
||||||
|
--color-indigo-50: 20 23 38;
|
||||||
|
--color-indigo-75: 24 36 73;
|
||||||
|
--color-indigo-100: 29 46 98;
|
||||||
|
--color-indigo-200: 37 57 116;
|
||||||
|
--color-indigo-300: 48 67 132;
|
||||||
|
--color-indigo-400: 58 79 151;
|
||||||
|
--color-indigo-500: 67 93 177;
|
||||||
|
--color-indigo-600: 62 99 221;
|
||||||
|
--color-indigo-700: 84 114 228;
|
||||||
|
--color-indigo-800: 158 177 255;
|
||||||
|
--color-indigo-900: 214 225 255;
|
||||||
|
|
||||||
|
--color-iris-25: 19 19 30;
|
||||||
|
--color-iris-50: 23 22 37;
|
||||||
|
--color-iris-75: 32 34 72;
|
||||||
|
--color-iris-100: 38 42 101;
|
||||||
|
--color-iris-200: 48 51 116;
|
||||||
|
--color-iris-300: 61 62 130;
|
||||||
|
--color-iris-400: 74 74 149;
|
||||||
|
--color-iris-500: 89 88 177;
|
||||||
|
--color-iris-600: 91 91 214;
|
||||||
|
--color-iris-700: 110 106 222;
|
||||||
|
--color-iris-800: 177 169 255;
|
||||||
|
--color-iris-900: 224 223 254;
|
||||||
|
|
||||||
|
--color-violet-25: 20 18 31;
|
||||||
|
--color-violet-50: 27 21 37;
|
||||||
|
--color-violet-75: 41 31 67;
|
||||||
|
--color-violet-100: 51 37 91;
|
||||||
|
--color-violet-200: 60 46 105;
|
||||||
|
--color-violet-300: 71 56 118;
|
||||||
|
--color-violet-400: 86 70 139;
|
||||||
|
--color-violet-500: 105 88 173;
|
||||||
|
--color-violet-600: 110 86 207;
|
||||||
|
--color-violet-700: 125 102 217;
|
||||||
|
--color-violet-800: 186 167 255;
|
||||||
|
--color-violet-900: 226 221 254;
|
||||||
|
|
||||||
|
--color-pink-25: 25 17 23;
|
||||||
|
--color-pink-50: 33 18 29;
|
||||||
|
--color-pink-75: 55 23 47;
|
||||||
|
--color-pink-100: 75 20 61;
|
||||||
|
--color-pink-200: 89 28 71;
|
||||||
|
--color-pink-300: 105 41 85;
|
||||||
|
--color-pink-400: 131 56 105;
|
||||||
|
--color-pink-500: 168 72 133;
|
||||||
|
--color-pink-600: 214 64 159;
|
||||||
|
--color-pink-700: 222 81 168;
|
||||||
|
--color-pink-800: 255 141 204;
|
||||||
|
--color-pink-900: 253 209 234;
|
||||||
|
--color-orange-25: 23 18 14;
|
||||||
|
--color-orange-50: 30 22 15;
|
||||||
|
--color-orange-75: 51 30 11;
|
||||||
|
--color-orange-100: 70 33 0;
|
||||||
|
--color-orange-200: 86 40 0;
|
||||||
|
--color-orange-300: 102 53 12;
|
||||||
|
--color-orange-400: 126 69 29;
|
||||||
|
--color-orange-500: 163 88 41;
|
||||||
|
--color-orange-600: 247 107 21;
|
||||||
|
--color-orange-700: 255 128 31;
|
||||||
|
--color-orange-800: 255 160 87;
|
||||||
|
--color-orange-900: 255 224 194;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,46 +1,7 @@
|
|||||||
.dropdown-pane {
|
.dropdown-pane {
|
||||||
@include elegant-card;
|
@apply border rounded-lg hidden relative invisible shadow-lg border-slate-25 dark:border-slate-700 box-content p-2 w-fit z-[9999];
|
||||||
@include border-light;
|
|
||||||
box-sizing: content-box;
|
|
||||||
padding: var(--space-small);
|
|
||||||
width: fit-content;
|
|
||||||
z-index: var(--z-index-very-high);
|
|
||||||
|
|
||||||
&.dropdown-pane--open {
|
&.dropdown-pane--open {
|
||||||
@apply bg-white dark:bg-slate-800;
|
@apply bg-white absolute dark:bg-slate-800 block visible;
|
||||||
display: block;
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.dropdowm--bottom {
|
|
||||||
&::before {
|
|
||||||
@include arrow(top, var(--color-border-light), 14px);
|
|
||||||
position: absolute;
|
|
||||||
right: 6px;
|
|
||||||
top: -14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
@include arrow(top, $color-white, var(--space-slab));
|
|
||||||
position: absolute;
|
|
||||||
right: var(--space-small);
|
|
||||||
top: -12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.dropdowm--top {
|
|
||||||
&::before {
|
|
||||||
@include arrow(bottom, var(--color-border-light), 14px);
|
|
||||||
bottom: -14px;
|
|
||||||
position: absolute;
|
|
||||||
right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
@include arrow(bottom, $color-white, var(--space-slab));
|
|
||||||
bottom: -12px;
|
|
||||||
position: absolute;
|
|
||||||
right: var(--space-small);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,6 +97,10 @@
|
|||||||
|
|
||||||
.multiselect__tags {
|
.multiselect__tags {
|
||||||
@apply bg-white dark:bg-slate-900 border border-solid border-slate-200 dark:border-slate-600 m-0 min-h-[2.875rem] pt-0;
|
@apply bg-white dark:bg-slate-900 border border-solid border-slate-200 dark:border-slate-600 m-0 min-h-[2.875rem] pt-0;
|
||||||
|
|
||||||
|
input {
|
||||||
|
@apply border-0 border-none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect__tags-wrap {
|
.multiselect__tags-wrap {
|
||||||
@@ -149,6 +153,28 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.multiselect-wrap--small {
|
.multiselect-wrap--small {
|
||||||
|
// To be removed one SLA reports date picker is created
|
||||||
|
&.tiny {
|
||||||
|
.multiselect.no-margin {
|
||||||
|
@apply min-h-[32px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.multiselect__select {
|
||||||
|
@apply min-h-[32px] h-8;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
@apply top-[60%];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.multiselect__tags {
|
||||||
|
@apply min-h-[32px] max-h-[32px];
|
||||||
|
|
||||||
|
.multiselect__single {
|
||||||
|
@apply pt-1 pb-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.multiselect__tags,
|
.multiselect__tags,
|
||||||
.multiselect__input,
|
.multiselect__input,
|
||||||
@@ -180,7 +206,6 @@
|
|||||||
.multiselect--disabled .multiselect__select {
|
.multiselect--disabled .multiselect__select {
|
||||||
@apply bg-transparent;
|
@apply bg-transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect-wrap--medium {
|
.multiselect-wrap--medium {
|
||||||
|
|||||||
@@ -13,20 +13,14 @@
|
|||||||
@import '~shared/assets/stylesheets/ionicons';
|
@import '~shared/assets/stylesheets/ionicons';
|
||||||
|
|
||||||
@import 'mixins';
|
@import 'mixins';
|
||||||
@import 'foundation-settings';
|
|
||||||
@import 'helper-classes';
|
@import 'helper-classes';
|
||||||
@import 'foundation-sites/scss/foundation';
|
|
||||||
|
|
||||||
@include foundation-prototype-spacing;
|
|
||||||
@include foundation-everything($flex: true);
|
|
||||||
|
|
||||||
@import 'typography';
|
@import 'typography';
|
||||||
@import 'layout';
|
@import 'layout';
|
||||||
@import 'animations';
|
@import 'animations';
|
||||||
|
|
||||||
@import 'foundation-custom';
|
|
||||||
@import 'widgets/buttons';
|
@import 'widgets/buttons';
|
||||||
@import 'widgets/forms';
|
@import 'widgets/base';
|
||||||
|
|
||||||
@import 'plugins/multiselect';
|
@import 'plugins/multiselect';
|
||||||
|
|
||||||
@@ -36,7 +30,6 @@
|
|||||||
@import 'tailwindcss/utilities';
|
@import 'tailwindcss/utilities';
|
||||||
@import 'widget/assets/scss/utilities';
|
@import 'widget/assets/scss/utilities';
|
||||||
|
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
font-family: 'PlusJakarta', sans-serif;
|
font-family: 'PlusJakarta', sans-serif;
|
||||||
|
|||||||
@@ -1,112 +1 @@
|
|||||||
.settings {
|
// to be removed
|
||||||
@apply overflow-auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wizard-box {
|
|
||||||
.item {
|
|
||||||
@apply cursor-pointer py-4 pr-4 pl-6 relative;
|
|
||||||
|
|
||||||
&::before,
|
|
||||||
&::after {
|
|
||||||
@apply bg-slate-75 dark:bg-slate-600 content-[''] h-full absolute top-5 w-0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
@apply h-4 top-0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
&::before {
|
|
||||||
@apply h-0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
&::after {
|
|
||||||
@apply h-0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
h3 {
|
|
||||||
@apply text-woot-500 dark:text-woot-500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step {
|
|
||||||
@apply bg-woot-500 dark:bg-woot-500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.over {
|
|
||||||
&::after {
|
|
||||||
@apply bg-woot-500 dark:bg-woot-500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step {
|
|
||||||
@apply bg-woot-500 dark:bg-woot-500;
|
|
||||||
}
|
|
||||||
|
|
||||||
& + .item {
|
|
||||||
&::before {
|
|
||||||
@apply bg-woot-500 dark:bg-woot-500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
@apply text-slate-800 dark:text-slate-100 text-base pl-6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.completed {
|
|
||||||
@apply text-green-500 dark:text-green-500 ml-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
@apply text-slate-600 dark:text-slate-300 text-sm m-0 pl-6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step {
|
|
||||||
@apply bg-slate-75 dark:bg-slate-600 rounded-2xl font-medium w-4 left-4 leading-4 z-[999] absolute text-center text-white dark:text-white text-xxs top-5;
|
|
||||||
|
|
||||||
i {
|
|
||||||
@apply text-xxs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.wizard-body {
|
|
||||||
@apply border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6;
|
|
||||||
|
|
||||||
&.height-auto {
|
|
||||||
@apply h-auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings--content {
|
|
||||||
@apply my-2 mx-8;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
@apply font-medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code {
|
|
||||||
@apply bg-slate-50 dark:bg-slate-800 overflow-auto p-2.5 whitespace-nowrap;
|
|
||||||
|
|
||||||
code {
|
|
||||||
@apply bg-transparent border-0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-init {
|
|
||||||
@apply pt-[30%] text-center;
|
|
||||||
|
|
||||||
p {
|
|
||||||
@apply p-6;
|
|
||||||
}
|
|
||||||
|
|
||||||
> a > img {
|
|
||||||
@apply w-60;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
// scss-lint:disable QualifyingElement
|
||||||
|
|
||||||
|
// Base typography
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
@apply font-medium text-slate-800 dark:text-slate-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
word-spacing: 0.12em;
|
||||||
|
|
||||||
|
@apply mb-2 leading-[1.65] text-sm;
|
||||||
|
|
||||||
|
a {
|
||||||
|
@apply text-woot-500 dark:text-woot-500 cursor-pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
@apply text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
@apply clear-both max-w-full h-0 my-5 mx-0 border-slate-300 dark:border-slate-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol,
|
||||||
|
dl {
|
||||||
|
@apply mb-2 list-disc list-outside leading-[1.65];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form elements
|
||||||
|
label {
|
||||||
|
@apply text-slate-800 dark:text-slate-200 block m-0 leading-7 text-sm font-medium;
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
input {
|
||||||
|
@apply mb-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrap,
|
||||||
|
.help-text {
|
||||||
|
@apply text-slate-800 dark:text-slate-100 text-sm font-medium;
|
||||||
|
|
||||||
|
.help-text {
|
||||||
|
@apply font-normal text-slate-600 dark:text-slate-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus outline removal
|
||||||
|
.button,
|
||||||
|
textarea,
|
||||||
|
input:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inputs
|
||||||
|
input[type='text']:not(.reset-base),
|
||||||
|
input[type='number']:not(.reset-base),
|
||||||
|
input[type='password']:not(.reset-base),
|
||||||
|
input[type='date']:not(.reset-base),
|
||||||
|
input[type='email']:not(.reset-base),
|
||||||
|
input[type='url']:not(.reset-base) {
|
||||||
|
@apply block box-border w-full transition-colors focus:border-woot-500 dark:focus:border-woot-600 duration-[0.25s] ease-[ease-in-out] h-10 appearance-none mx-0 mt-0 mb-4 p-2 rounded-md text-base font-normal bg-white dark:bg-slate-900 focus:bg-white focus:dark:bg-slate-900 text-slate-900 dark:text-slate-100 border border-solid border-slate-200 dark:border-slate-600;
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
@apply bg-slate-200 dark:bg-slate-700 text-slate-400 dark:text-slate-400 border-slate-200 dark:border-slate-600 cursor-not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='file'] {
|
||||||
|
@apply bg-white dark:bg-slate-800 leading-[1.15] mb-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select
|
||||||
|
select {
|
||||||
|
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='32' height='24' viewBox='0 0 32 24'><polygon points='0,0 32,0 16,24' style='fill: rgb%28110, 111, 115%29'></polygon></svg>");
|
||||||
|
background-position: right -1rem center;
|
||||||
|
background-size: 9px 6px;
|
||||||
|
@apply h-10 mx-0 mt-0 mb-4 bg-origin-content focus-visible:outline-none bg-no-repeat py-2 pr-6 pl-2 rounded-md w-full text-base font-normal appearance-none transition-colors focus:border-woot-500 dark:focus:border-woot-600 duration-[0.25s] ease-[ease-in-out] bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100 border border-solid border-slate-200 dark:border-slate-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Textarea
|
||||||
|
textarea {
|
||||||
|
@apply block box-border w-full transition-colors focus:border-woot-500 dark:focus:border-woot-600 duration-[0.25s] ease-[ease-in-out] h-16 appearance-none mx-0 mt-0 mb-4 p-2 rounded-md text-base font-normal bg-white dark:bg-slate-900 focus:bg-white focus:dark:bg-slate-900 text-slate-900 dark:text-slate-100 border border-solid border-slate-200 dark:border-slate-600;
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
@apply bg-slate-200 dark:bg-slate-700 text-slate-400 dark:text-slate-400 border-slate-200 dark:border-slate-600 cursor-not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error handling
|
||||||
|
.has-multi-select-error {
|
||||||
|
div.multiselect {
|
||||||
|
@apply mb-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
input,
|
||||||
|
input:not([type]),
|
||||||
|
textarea,
|
||||||
|
select,
|
||||||
|
.multiselect > .multiselect__tags,
|
||||||
|
.multiselect:not(.no-margin) {
|
||||||
|
@apply border border-solid border-red-400 dark:border-red-400 mb-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
@apply text-red-400 dark:text-red-400 block text-sm mb-2.5 w-full;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group.small {
|
||||||
|
input {
|
||||||
|
@apply text-sm h-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
@apply border-red-400 dark:border-red-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code styling
|
||||||
|
code {
|
||||||
|
font-family: 'ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas',
|
||||||
|
'"Liberation Mono"', '"Courier New"', 'monospace';
|
||||||
|
@apply text-xs border-0;
|
||||||
|
|
||||||
|
&.hljs {
|
||||||
|
@apply bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-50 rounded-lg p-5;
|
||||||
|
|
||||||
|
.hljs-number,
|
||||||
|
.hljs-string {
|
||||||
|
@apply text-red-800 dark:text-red-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,40 @@
|
|||||||
|
// scss-lint:disable SpaceAfterPropertyColon
|
||||||
|
// scss-lint:disable MergeableSelector
|
||||||
|
button {
|
||||||
|
font-family: inherit;
|
||||||
|
transition:
|
||||||
|
background-color 0.25s ease-out,
|
||||||
|
color 0.25s ease-out;
|
||||||
|
@apply inline-block items-center mb-0 text-center align-middle cursor-pointer text-sm mt-0 mx-0 py-1 px-2.5 border border-solid border-transparent dark:border-transparent rounded-[0.3125rem];
|
||||||
|
|
||||||
|
&:disabled,
|
||||||
|
&.disabled {
|
||||||
|
@apply opacity-40 cursor-not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
@apply mb-0 flex flex-nowrap items-stretch;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
@apply m-0 text-sm rounded-none first:rounded-tl-[0.3125rem] first:rounded-bl-[0.3125rem] last:rounded-tr-[0.3125rem] last:rounded-br-[0.3125rem] rtl:space-x-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button--only-icon {
|
||||||
|
@apply w-10 justify-center pl-0 pr-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-button {
|
||||||
|
@apply m-0;
|
||||||
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
@apply items-center inline-flex h-10 mb-0 gap-2;
|
@apply items-center bg-woot-500 dark:bg-woot-500 px-2.5 text-white dark:text-white inline-flex h-10 mb-0 gap-2 font-medium;
|
||||||
|
|
||||||
.button__content {
|
.button__content {
|
||||||
@apply w-full;
|
@apply w-full whitespace-nowrap overflow-hidden text-ellipsis;
|
||||||
|
|
||||||
img,
|
img,
|
||||||
svg {
|
svg {
|
||||||
@@ -10,12 +42,61 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@apply bg-woot-600 dark:bg-woot-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled,
|
||||||
|
&.disabled {
|
||||||
|
@apply opacity-40 cursor-not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
@apply bg-[#44ce4b] dark:bg-[#44ce4b] text-white dark:text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.secondary {
|
||||||
|
@apply bg-slate-700 dark:bg-slate-600 text-white dark:text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
@apply bg-woot-500 dark:bg-woot-500 text-white dark:text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.clear {
|
||||||
|
@apply text-woot-500 dark:text-woot-500 bg-transparent dark:bg-transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.alert {
|
||||||
|
@apply bg-red-500 dark:bg-red-500 text-white dark:text-white;
|
||||||
|
|
||||||
|
&.clear {
|
||||||
|
@apply bg-transparent dark:bg-transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
@apply bg-[#ffc532] dark:bg-[#ffc532] text-white dark:text-white;
|
||||||
|
|
||||||
|
&.clear {
|
||||||
|
@apply bg-transparent dark:bg-transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tiny {
|
||||||
|
@apply h-6 text-[10px];
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
@apply h-8 text-xs;
|
||||||
|
}
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
@apply px-2 py-0;
|
@apply px-2 py-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODDO - Remove after moving all buttons to woot-button
|
// @TODDO - Remove after moving all buttons to woot-button
|
||||||
.icon + .button__content {
|
.icon+.button__content {
|
||||||
@apply w-auto;
|
@apply w-auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,10 +115,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.hollow {
|
&.hollow {
|
||||||
@apply border border-woot-500 dark:border-woot-500 text-woot-500 dark:text-woot-500 hover:bg-woot-50 dark:hover:bg-woot-900;
|
@apply border border-woot-500 bg-transparent dark:bg-transparent dark:border-woot-500 text-woot-500 dark:text-woot-500 hover:bg-woot-50 dark:hover:bg-woot-900;
|
||||||
|
|
||||||
&.secondary {
|
&.secondary {
|
||||||
@apply text-slate-700 border-slate-200 dark:border-slate-600 dark:text-slate-100 hover:bg-slate-50 dark:hover:bg-slate-700;
|
@apply text-slate-700 border-slate-100 dark:border-slate-800 dark:text-slate-100 hover:bg-slate-50 dark:hover:bg-slate-700;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.success {
|
&.success {
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
// File to be removed
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
@keyframes left-shift-animation {
|
|
||||||
0%,
|
|
||||||
100% {
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
50% {
|
|
||||||
transform: translateX(1px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversation {
|
|
||||||
&.active {
|
|
||||||
animation: left-shift-animation 0.25s $swift-ease-out-function;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
@apply rounded-r-lg rounded-l mr-auto break-words;
|
@apply rounded-r-lg rounded-l mr-auto break-words;
|
||||||
|
|
||||||
&:not(.is-unsupported) {
|
&:not(.is-unsupported) {
|
||||||
@apply border border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-700 text-black-900 dark:text-slate-50
|
@apply border border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-700 text-black-900 dark:text-slate-50;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-image {
|
&.is-image {
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.file {
|
.file {
|
||||||
.text-block-title {
|
.attachment-name {
|
||||||
@apply text-slate-700 dark:text-woot-300;
|
@apply text-slate-700 dark:text-woot-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,20 +222,6 @@
|
|||||||
@apply flex relative flex-col;
|
@apply flex relative flex-col;
|
||||||
}
|
}
|
||||||
|
|
||||||
.typing-indicator-wrap {
|
|
||||||
@apply items-center flex h-0 absolute w-full -top-8;
|
|
||||||
|
|
||||||
.typing-indicator {
|
|
||||||
@include elegant-card;
|
|
||||||
@include round-corner;
|
|
||||||
@apply py-2 pr-4 pl-5 bg-white dark:bg-slate-700 text-slate-800 dark:text-slate-100 text-xs font-semibold my-2.5 mx-auto;
|
|
||||||
|
|
||||||
.gif {
|
|
||||||
@apply ml-2 w-6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.left .bubble .text-content {
|
.left .bubble .text-content {
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
// scss-lint:disable QualifyingElement
|
|
||||||
|
|
||||||
label {
|
|
||||||
@apply text-slate-800 dark:text-slate-200;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
@apply bg-white dark:bg-slate-900 focus:bg-white focus:dark:bg-slate-900 text-slate-900 dark:text-slate-100 border-slate-200 dark:border-slate-600;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
@apply bg-white dark:bg-slate-900 focus:bg-white focus:dark:bg-slate-900 text-slate-900 dark:text-slate-100 border-slate-200 dark:border-slate-600;
|
|
||||||
|
|
||||||
&[disabled] {
|
|
||||||
@apply bg-slate-200 dark:bg-slate-700 text-slate-400 dark:text-slate-400 border-slate-200 dark:border-slate-600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type='file'] {
|
|
||||||
@apply bg-white dark:bg-slate-800;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
@apply bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100 border-slate-200 dark:border-slate-600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
input[type='color'],
|
|
||||||
input[type='date'],
|
|
||||||
input[type='datetime'],
|
|
||||||
input[type='datetime-local'],
|
|
||||||
input[type='email'],
|
|
||||||
input[type='month'],
|
|
||||||
input[type='number'],
|
|
||||||
input[type='password'],
|
|
||||||
input[type='search'],
|
|
||||||
input[type='tel'],
|
|
||||||
input[type='text'],
|
|
||||||
input[type='time'],
|
|
||||||
input[type='url'],
|
|
||||||
input[type='week'],
|
|
||||||
input:not([type]),
|
|
||||||
textarea,
|
|
||||||
select,
|
|
||||||
.multiselect > .multiselect__tags {
|
|
||||||
@apply border border-solid border-red-400 dark:border-red-400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
@apply text-red-400 dark:text-red-400 block text-sm mb-2.5 w-full;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.button,
|
|
||||||
textarea,
|
|
||||||
input {
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-wrap {
|
|
||||||
@apply text-slate-800 dark:text-slate-100 text-sm font-medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-text {
|
|
||||||
@apply font-normal text-slate-600 dark:text-slate-400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group.small {
|
|
||||||
input {
|
|
||||||
@apply text-sm h-8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
@apply border-red-400 dark:border-red-400;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
.auth-wrap {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Outside login wrapper
|
|
||||||
.login {
|
|
||||||
@include full-height;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding-top: $space-larger * 1.2;
|
|
||||||
|
|
||||||
.login__hero {
|
|
||||||
margin-bottom: $space-larger;
|
|
||||||
|
|
||||||
.hero__logo {
|
|
||||||
width: 180px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero__title {
|
|
||||||
font-weight: $font-weight-light;
|
|
||||||
margin-top: $space-larger;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero__sub {
|
|
||||||
color: $medium-gray;
|
|
||||||
font-size: $font-size-medium;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Login box
|
|
||||||
.login-box {
|
|
||||||
@include background-white;
|
|
||||||
@include border-normal;
|
|
||||||
@include elegant-card;
|
|
||||||
|
|
||||||
border-radius: $space-smaller;
|
|
||||||
padding: $space-large;
|
|
||||||
|
|
||||||
label {
|
|
||||||
color: $color-gray;
|
|
||||||
font-size: $font-size-default;
|
|
||||||
|
|
||||||
input {
|
|
||||||
font-size: $font-size-default;
|
|
||||||
height: $space-larger;
|
|
||||||
padding: $space-slab;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
font-size: $font-size-small;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
height: $space-larger;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sigin__footer {
|
|
||||||
font-size: $font-size-default;
|
|
||||||
padding: $space-medium;
|
|
||||||
|
|
||||||
> a {
|
|
||||||
font-weight: $font-weight-bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
.modal-mask {
|
|
||||||
// @include flex;
|
|
||||||
// @include flex-align(center, middle);
|
|
||||||
@apply flex items-center justify-center bg-modal-backdrop-light dark:bg-modal-backdrop-dark z-[9990] h-full left-0 fixed top-0 w-full;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-top-bar {
|
|
||||||
@apply px-8 pt-9 pb-0;
|
|
||||||
|
|
||||||
img {
|
|
||||||
@apply max-h-[3.75rem];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-container {
|
|
||||||
@apply shadow-md rounded-sm max-h-full overflow-auto relative w-[37.5rem];
|
|
||||||
|
|
||||||
&.medium {
|
|
||||||
@apply max-w-[80%] w-[56.25rem];
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-box {
|
|
||||||
@apply h-auto p-0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
@apply text-slate-800 dark:text-slate-100 text-lg font-semibold;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
@apply text-sm m-0 p-0 text-slate-600 mt-2 text-sm dark:text-slate-300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
@apply p-8;
|
|
||||||
}
|
|
||||||
|
|
||||||
form,
|
|
||||||
.modal-content {
|
|
||||||
@apply pt-4 pb-8 px-8 self-center;
|
|
||||||
|
|
||||||
a {
|
|
||||||
@apply p-4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-footer {
|
|
||||||
// @include flex;
|
|
||||||
// @include flex-align($x: flex-end, $y: middle);
|
|
||||||
@apply flex justify-end items-center py-2 px-0 gap-2;
|
|
||||||
|
|
||||||
&.justify-content-end {
|
|
||||||
@apply justify-end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-item {
|
|
||||||
@apply p-8;
|
|
||||||
|
|
||||||
button {
|
|
||||||
@apply m-0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-enter,
|
|
||||||
.modal-leave {
|
|
||||||
@apply opacity-0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-enter .modal-container,
|
|
||||||
.modal-leave .modal-container {
|
|
||||||
transform: scale(1.1);
|
|
||||||
// @apply transform scale-110;
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
.reply-box {
|
|
||||||
transition: box-shadow 0.35s $swift-ease-out-function,
|
|
||||||
height 2s $swift-ease-out-function;
|
|
||||||
|
|
||||||
&.is-focused {
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reply-box__top {
|
|
||||||
.icon {
|
|
||||||
color: $medium-gray;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: $font-size-medium;
|
|
||||||
margin-right: $space-small;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: $color-woot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.attachment {
|
|
||||||
cursor: pointer;
|
|
||||||
margin-right: $space-one;
|
|
||||||
padding: 0 $space-small;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-js {
|
|
||||||
background: transparent;
|
|
||||||
// Override min-height : 50px in foundation
|
|
||||||
//
|
|
||||||
max-height: $space-mega * 2.4;
|
|
||||||
min-height: 3rem;
|
|
||||||
padding: var(--space-normal) 0 0;
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
> textarea {
|
|
||||||
@include ghost-input();
|
|
||||||
background: transparent;
|
|
||||||
margin: 0;
|
|
||||||
max-height: $space-mega * 2.4;
|
|
||||||
// Override min-height : 50px in foundation
|
|
||||||
min-height: 3rem;
|
|
||||||
padding: var(--space-normal) 0 0;
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-private {
|
|
||||||
@apply bg-yellow-100 dark:bg-yellow-800;
|
|
||||||
|
|
||||||
.reply-box__top {
|
|
||||||
@apply bg-yellow-100 dark:bg-yellow-800;
|
|
||||||
|
|
||||||
> input {
|
|
||||||
@apply bg-yellow-100 dark:bg-yellow-800;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
.report-card {
|
|
||||||
@include custom-border-top(3px, transparent);
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
margin: 0;
|
|
||||||
padding: var(--space-normal);
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
@include custom-border-top(3px, var(--color-woot));
|
|
||||||
@include background-white;
|
|
||||||
.heading,
|
|
||||||
.metric {
|
|
||||||
color: var(--color-woot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading {
|
|
||||||
align-items: center;
|
|
||||||
color: var(--color-heading);
|
|
||||||
display: flex;
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
font-weight: var(--font-weight-bold);
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-icon {
|
|
||||||
color: var(--b-400);
|
|
||||||
margin-left: var(--space-micro);
|
|
||||||
}
|
|
||||||
|
|
||||||
.metric-wrap {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metric {
|
|
||||||
font-size: var(--font-size-big);
|
|
||||||
font-weight: var(--font-weight-feather);
|
|
||||||
margin-top: var(--space-smaller);
|
|
||||||
}
|
|
||||||
|
|
||||||
.metric-trend {
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
margin: 0 var(--space-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.metric-up {
|
|
||||||
color: $success-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metric-down {
|
|
||||||
color: $alert-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desc {
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
margin: 0;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
.reports-option__rounded--item {
|
|
||||||
border-radius: 100%;
|
|
||||||
height: var(--space-two);
|
|
||||||
width: var(--space-two);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reports-option__item {
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-right: var(--space-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reports-option__label--swatch {
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reports-option__wrap {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reports-option__title {
|
|
||||||
margin: 0 var(--space-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.switch {
|
|
||||||
margin-bottom: var(--space-zero);
|
|
||||||
margin-left: var(--space-small);
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
.search {
|
|
||||||
@include flex;
|
|
||||||
@include flex-align($x: left, $y: middle);
|
|
||||||
@include flex-shrink;
|
|
||||||
|
|
||||||
padding: $space-one $space-normal;
|
|
||||||
transition: all 0.3s var(--ease-in-out-quad);
|
|
||||||
|
|
||||||
> .icon {
|
|
||||||
color: $medium-gray;
|
|
||||||
font-size: $font-size-medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
> input {
|
|
||||||
@include ghost-input();
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
.side-menu {
|
|
||||||
i {
|
|
||||||
margin-right: var(--space-smaller);
|
|
||||||
min-width: var(--space-two);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
z-index: 1024 - 1;
|
|
||||||
|
|
||||||
//logo
|
|
||||||
.logo {
|
|
||||||
img {
|
|
||||||
max-height: 108px;
|
|
||||||
padding: $woot-logo-padding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nested {
|
|
||||||
a {
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
margin-bottom: var(--space-micro);
|
|
||||||
margin-top: var(--space-micro);
|
|
||||||
|
|
||||||
.inbox-icon {
|
|
||||||
display: inline-block;
|
|
||||||
margin-right: var(--space-micro);
|
|
||||||
min-width: var(--space-normal);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bottom-nav
|
|
||||||
.bottom-nav {
|
|
||||||
@include flex;
|
|
||||||
@include space-between-column;
|
|
||||||
@include border-normal-top;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: var(--space-one) var(--space-normal) var(--space-one)
|
|
||||||
var(--space-one);
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--color-background-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-pane {
|
|
||||||
bottom: 3.75rem;
|
|
||||||
display: block;
|
|
||||||
visibility: visible;
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active {
|
|
||||||
border-bottom: 2px solid $medium-gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hamburger--menu {
|
|
||||||
cursor: pointer;
|
|
||||||
display: block;
|
|
||||||
margin-right: var(--space-normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header--icon {
|
|
||||||
display: block;
|
|
||||||
margin: 0 var(--space-small) 0 var(--space-smaller);
|
|
||||||
|
|
||||||
@media screen and (max-width: 1200px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-title {
|
|
||||||
margin: 0 var(--space-small);
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
.ui-snackbar-container {
|
|
||||||
left: 0;
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 25rem;
|
|
||||||
overflow: hidden;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
text-align: center;
|
|
||||||
top: $space-normal;
|
|
||||||
z-index: 9999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-snackbar {
|
|
||||||
@include shadow;
|
|
||||||
background-color: $woot-snackbar-bg;
|
|
||||||
border-radius: $space-smaller;
|
|
||||||
display: inline-flex;
|
|
||||||
margin-bottom: $space-small;
|
|
||||||
max-width: 25rem;
|
|
||||||
min-height: 1.875rem;
|
|
||||||
min-width: 15rem;
|
|
||||||
padding: $space-slab $space-medium;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-snackbar-text {
|
|
||||||
color: $color-white;
|
|
||||||
font-size: $font-size-small;
|
|
||||||
font-weight: $font-weight-medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-snackbar-action {
|
|
||||||
margin-left: auto;
|
|
||||||
padding-left: 1.875rem;
|
|
||||||
|
|
||||||
button {
|
|
||||||
background: none;
|
|
||||||
border: 0;
|
|
||||||
color: $woot-snackbar-button;
|
|
||||||
font-size: $font-size-small;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
// To be removed
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
.status-bar {
|
|
||||||
@include flex;
|
|
||||||
@include flex-align($x: center, $y: middle);
|
|
||||||
background: lighten($warning-color, 36%);
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 0;
|
|
||||||
padding: $space-normal $space-smaller;
|
|
||||||
|
|
||||||
.message {
|
|
||||||
font-weight: $font-weight-medium;
|
|
||||||
margin-bottom: $zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
margin: $space-smaller $zero $zero;
|
|
||||||
padding: $space-small $space-normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.danger {
|
|
||||||
background: lighten($alert-color, 30%);
|
|
||||||
|
|
||||||
.button {
|
|
||||||
// Default and disabled states
|
|
||||||
&,
|
|
||||||
&.disabled,
|
|
||||||
&[disabled],
|
|
||||||
&.disabled:hover,
|
|
||||||
&[disabled]:hover,
|
|
||||||
&.disabled:focus,
|
|
||||||
&[disabled]:focus {
|
|
||||||
background-color: $alert-color;
|
|
||||||
color: $color-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background-color: darken($alert-color, 7%);
|
|
||||||
color: $color-white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.warning {
|
|
||||||
background: lighten($warning-color, 36%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tabs {
|
.tabs {
|
||||||
@apply border-r-0 border-l-0 border-t-0 flex min-w-[6.25rem] py-0 px-4;
|
@apply border-r-0 border-l-0 border-t-0 flex min-w-[6.25rem] py-0 px-4 list-none mb-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs--with-scroll {
|
.tabs--with-scroll {
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tabs-title {
|
.tabs-title {
|
||||||
@apply flex-shrink-0 my-0 mx-2 ;
|
@apply flex-shrink-0 my-0 mx-2;
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
@apply bg-slate-50 dark:bg-slate-800 rounded-md text-slate-600 dark:text-slate-100 h-5 flex items-center justify-center text-xxs font-semibold my-0 mx-1 px-1 py-0;
|
@apply bg-slate-50 dark:bg-slate-800 rounded-md text-slate-600 dark:text-slate-100 h-5 flex items-center justify-center text-xxs font-semibold my-0 mx-1 px-1 py-0;
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@apply flex items-center flex-row border-b border-transparent text-slate-500 dark:text-slate-200 text-sm top-[1px] relative;
|
@apply flex items-center flex-row border-b py-2.5 select-none cursor-pointer border-transparent text-slate-500 dark:text-slate-200 text-sm top-[1px] relative;
|
||||||
transition: border-color 0.15s $swift-ease-out-function;
|
transition: border-color 0.15s $swift-ease-out-function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
table {
|
table {
|
||||||
@apply border-spacing-0 text-sm;
|
@apply border-spacing-0 text-sm w-full;
|
||||||
|
|
||||||
thead {
|
thead {
|
||||||
th {
|
th {
|
||||||
@apply font-semibold tracking-[1px] text-left uppercase text-slate-900 dark:text-slate-200;
|
@apply font-semibold tracking-[1px] text-left px-2.5 uppercase text-slate-900 dark:text-slate-200;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="-mt-px text-sm">
|
<div class="-mt-px text-sm">
|
||||||
<button
|
<button
|
||||||
class="flex items-center select-none w-full bg-slate-50 dark:bg-slate-800 border border-l-0 border-r-0 border-solid m-0 border-slate-100 dark:border-slate-700/50 cursor-grab justify-between py-2 px-4 drag-handle"
|
class="flex items-center select-none w-full rounded-none bg-slate-50 dark:bg-slate-800 border border-l-0 border-r-0 border-solid m-0 border-slate-100 dark:border-slate-700/50 cursor-grab justify-between py-2 px-4 drag-handle"
|
||||||
@click="$emit('click')"
|
@click="$emit('click')"
|
||||||
>
|
>
|
||||||
<div class="flex justify-between mb-0.5">
|
<div class="flex justify-between mb-0.5">
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
class="sm:w-[50%] md:w-[34%] lg:w-[25%] channel"
|
class="sm:w-[50%] md:w-1/3 lg:w-1/4 bg-white dark:bg-slate-900 cursor-pointer flex flex-col transition-all duration-200 ease-in -m-px py-4 px-0 items-center border border-solid border-slate-25 dark:border-slate-800 hover:border-woot-500 dark:hover:border-woot-500 hover:shadow-md hover:z-50 disabled:opacity-60"
|
||||||
@click="$emit('click')"
|
@click="$emit('click')"
|
||||||
>
|
>
|
||||||
<img :src="src" :alt="title" />
|
<img :src="src" :alt="title" class="w-[50%] my-4 mx-auto" />
|
||||||
<h3 class="channel__title">
|
<h3
|
||||||
|
class="text-slate-800 dark:text-slate-100 text-base text-center capitalize"
|
||||||
|
>
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</h3>
|
</h3>
|
||||||
</button>
|
</button>
|
||||||
@@ -31,32 +33,8 @@ export default {
|
|||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.channel:hover {
|
&:hover {
|
||||||
@apply border-transparent shadow-none cursor-not-allowed;
|
@apply border-transparent shadow-none cursor-not-allowed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel {
|
|
||||||
@apply bg-white dark:bg-slate-900 cursor-pointer flex flex-col transition-all duration-200 ease-in -m-px py-4 px-0 items-center border border-solid border-slate-25 dark:border-slate-800;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
@apply border-woot-500 dark:border-woot-500 shadow-md z-50;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
@apply opacity-60;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
@apply w-[50%] my-4 mx-auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel__title {
|
|
||||||
@apply text-slate-800 dark:text-slate-100 text-base text-center capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
@apply text-slate-600 dark:text-slate-300 w-full text-sm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="conversations-list-wrap flex-basis-clamp flex-shrink-0 overflow-hidden flex flex-col border-r rtl:border-r-0 rtl:border-l border-slate-50 dark:border-slate-800/50"
|
class="flex flex-col flex-shrink-0 overflow-hidden border-r conversations-list-wrap rtl:border-r-0 rtl:border-l border-slate-50 dark:border-slate-800/50"
|
||||||
:class="{
|
:class="[
|
||||||
hide: !showConversationList,
|
{ hidden: !showConversationList },
|
||||||
'list--full-width': isOnExpandedLayout,
|
isOnExpandedLayout ? 'basis-full' : 'flex-basis-clamp',
|
||||||
}"
|
]"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between py-0 px-4"
|
class="flex items-center justify-between px-4 py-0"
|
||||||
:class="{
|
:class="{
|
||||||
'pb-3 border-b border-slate-75 dark:border-slate-700':
|
'pb-3 border-b border-slate-75 dark:border-slate-700':
|
||||||
hasAppliedFiltersOrActiveFolders,
|
hasAppliedFiltersOrActiveFolders,
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
>
|
>
|
||||||
<div class="flex max-w-[85%] justify-center items-center">
|
<div class="flex max-w-[85%] justify-center items-center">
|
||||||
<h1
|
<h1
|
||||||
class="text-xl break-words overflow-hidden whitespace-nowrap text-ellipsis text-black-900 dark:text-slate-100 mb-0"
|
class="text-xl font-medium break-words truncate text-black-900 dark:text-slate-100"
|
||||||
:title="pageTitle"
|
:title="pageTitle"
|
||||||
>
|
>
|
||||||
{{ pageTitle }}
|
{{ pageTitle }}
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
|
|
||||||
<p
|
<p
|
||||||
v-if="!chatListLoading && !conversationList.length"
|
v-if="!chatListLoading && !conversationList.length"
|
||||||
class="overflow-auto p-4 flex justify-center items-center"
|
class="flex items-center justify-center p-4 overflow-auto"
|
||||||
>
|
>
|
||||||
{{ $t('CHAT_LIST.LIST.404') }}
|
{{ $t('CHAT_LIST.LIST.404') }}
|
||||||
</p>
|
</p>
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
ref="conversationList"
|
ref="conversationList"
|
||||||
class="conversations-list flex-1"
|
class="flex-1 conversations-list"
|
||||||
:class="{ 'overflow-hidden': isContextMenuOpen }"
|
:class="{ 'overflow-hidden': isContextMenuOpen }"
|
||||||
>
|
>
|
||||||
<virtual-list
|
<virtual-list
|
||||||
@@ -136,14 +136,17 @@
|
|||||||
:data-sources="conversationList"
|
:data-sources="conversationList"
|
||||||
:data-component="itemComponent"
|
:data-component="itemComponent"
|
||||||
:extra-props="virtualListExtraProps"
|
:extra-props="virtualListExtraProps"
|
||||||
class="w-full overflow-auto h-full"
|
class="w-full h-full overflow-auto"
|
||||||
footer-tag="div"
|
footer-tag="div"
|
||||||
>
|
>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div v-if="chatListLoading" class="text-center">
|
<div v-if="chatListLoading" class="text-center">
|
||||||
<span class="spinner mt-4 mb-4" />
|
<span class="mt-4 mb-4 spinner" />
|
||||||
</div>
|
</div>
|
||||||
<p v-if="showEndOfListMessage" class="text-center text-muted p-4">
|
<p
|
||||||
|
v-if="showEndOfListMessage"
|
||||||
|
class="p-4 text-center text-slate-400 dark:text-slate-300"
|
||||||
|
>
|
||||||
{{ $t('CHAT_LIST.EOF') }}
|
{{ $t('CHAT_LIST.EOF') }}
|
||||||
</p>
|
</p>
|
||||||
<intersection-observer
|
<intersection-observer
|
||||||
@@ -182,7 +185,7 @@ import ConversationBasicFilter from './widgets/conversation/ConversationBasicFil
|
|||||||
import ChatTypeTabs from './widgets/ChatTypeTabs.vue';
|
import ChatTypeTabs from './widgets/ChatTypeTabs.vue';
|
||||||
import ConversationItem from './ConversationItem.vue';
|
import ConversationItem from './ConversationItem.vue';
|
||||||
import timeMixin from '../mixins/time';
|
import timeMixin from '../mixins/time';
|
||||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
||||||
import conversationMixin from '../mixins/conversations';
|
import conversationMixin from '../mixins/conversations';
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
import advancedFilterTypes from './widgets/conversation/advancedFilterItems';
|
import advancedFilterTypes from './widgets/conversation/advancedFilterItems';
|
||||||
@@ -196,11 +199,6 @@ import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
|||||||
import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
||||||
import countries from 'shared/constants/countries';
|
import countries from 'shared/constants/countries';
|
||||||
import { generateValuesForEditCustomViews } from 'dashboard/helper/customViewsHelper';
|
import { generateValuesForEditCustomViews } from 'dashboard/helper/customViewsHelper';
|
||||||
|
|
||||||
import {
|
|
||||||
hasPressedAltAndJKey,
|
|
||||||
hasPressedAltAndKKey,
|
|
||||||
} from 'shared/helpers/KeyboardHelpers';
|
|
||||||
import { conversationListPageURL } from '../helper/URLHelper';
|
import { conversationListPageURL } from '../helper/URLHelper';
|
||||||
import {
|
import {
|
||||||
isOnMentionsView,
|
isOnMentionsView,
|
||||||
@@ -225,7 +223,7 @@ export default {
|
|||||||
mixins: [
|
mixins: [
|
||||||
timeMixin,
|
timeMixin,
|
||||||
conversationMixin,
|
conversationMixin,
|
||||||
eventListenerMixins,
|
keyboardEventListenerMixins,
|
||||||
alertMixin,
|
alertMixin,
|
||||||
filterMixin,
|
filterMixin,
|
||||||
uiSettingsMixin,
|
uiSettingsMixin,
|
||||||
@@ -688,30 +686,40 @@ export default {
|
|||||||
lastConversationIndex,
|
lastConversationIndex,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleKeyEvents(e) {
|
handlePreviousConversation() {
|
||||||
if (hasPressedAltAndJKey(e)) {
|
const { allConversations, activeConversationIndex } =
|
||||||
const { allConversations, activeConversationIndex } =
|
this.getKeyboardListenerParams();
|
||||||
this.getKeyboardListenerParams();
|
if (activeConversationIndex === -1) {
|
||||||
if (activeConversationIndex === -1) {
|
allConversations[0].click();
|
||||||
allConversations[0].click();
|
|
||||||
}
|
|
||||||
if (activeConversationIndex >= 1) {
|
|
||||||
allConversations[activeConversationIndex - 1].click();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (hasPressedAltAndKKey(e)) {
|
if (activeConversationIndex >= 1) {
|
||||||
const {
|
allConversations[activeConversationIndex - 1].click();
|
||||||
allConversations,
|
|
||||||
activeConversationIndex,
|
|
||||||
lastConversationIndex,
|
|
||||||
} = this.getKeyboardListenerParams();
|
|
||||||
if (activeConversationIndex === -1) {
|
|
||||||
allConversations[lastConversationIndex].click();
|
|
||||||
} else if (activeConversationIndex < lastConversationIndex) {
|
|
||||||
allConversations[activeConversationIndex + 1].click();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleNextConversation() {
|
||||||
|
const {
|
||||||
|
allConversations,
|
||||||
|
activeConversationIndex,
|
||||||
|
lastConversationIndex,
|
||||||
|
} = this.getKeyboardListenerParams();
|
||||||
|
if (activeConversationIndex === -1) {
|
||||||
|
allConversations[lastConversationIndex].click();
|
||||||
|
} else if (activeConversationIndex < lastConversationIndex) {
|
||||||
|
allConversations[activeConversationIndex + 1].click();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getKeyboardEvents() {
|
||||||
|
return {
|
||||||
|
'Alt+KeyJ': {
|
||||||
|
action: () => this.handlePreviousConversation(),
|
||||||
|
allowOnFocusedInput: true,
|
||||||
|
},
|
||||||
|
'Alt+KeyK': {
|
||||||
|
action: () => this.handleNextConversation(),
|
||||||
|
allowOnFocusedInput: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
resetAndFetchData() {
|
resetAndFetchData() {
|
||||||
this.appliedFilter = [];
|
this.appliedFilter = [];
|
||||||
this.resetBulkActions();
|
this.resetBulkActions();
|
||||||
@@ -1031,24 +1039,10 @@ export default {
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.conversations-list-wrap {
|
|
||||||
&.hide {
|
|
||||||
@apply hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.list--full-width {
|
|
||||||
@apply basis-full;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversations-list {
|
.conversations-list {
|
||||||
@apply overflow-hidden hover:overflow-y-auto;
|
@apply overflow-hidden hover:overflow-y-auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.load-more--button {
|
|
||||||
@apply text-center rounded-none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab--chat-type {
|
.tab--chat-type {
|
||||||
@apply py-0 px-4;
|
@apply py-0 px-4;
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="py-3 px-4">
|
<div class="py-3 px-4">
|
||||||
<div class="flex items-center mb-1">
|
<div class="flex items-center mb-1">
|
||||||
<h4 class="text-block-title flex items-center m-0 w-full error">
|
<h4 class="text-sm flex items-center m-0 w-full error">
|
||||||
<div v-if="isAttributeTypeCheckbox" class="checkbox-wrap">
|
<div v-if="isAttributeTypeCheckbox" class="flex items-center">
|
||||||
<input
|
<input
|
||||||
v-model="editedValue"
|
v-model="editedValue"
|
||||||
class="checkbox"
|
class="!my-0 mr-2 ml-0"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@change="onUpdate"
|
@change="onUpdate"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between w-full">
|
<div class="flex items-center justify-between w-full">
|
||||||
<span
|
<span
|
||||||
class="attribute-name"
|
class="w-full font-medium text-sm mb-0"
|
||||||
:class="{ error: $v.editedValue.$error }"
|
:class="
|
||||||
|
$v.editedValue.$error
|
||||||
|
? 'text-red-400 dark:text-red-500'
|
||||||
|
: 'text-slate-800 dark:text-slate-100'
|
||||||
|
"
|
||||||
>
|
>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</span>
|
</span>
|
||||||
<woot-button
|
<woot-button
|
||||||
v-if="showActions"
|
v-if="showCopyAndDeleteButton"
|
||||||
v-tooltip.left="$t('CUSTOM_ATTRIBUTES.ACTIONS.DELETE')"
|
v-tooltip.left="$t('CUSTOM_ATTRIBUTES.ACTIONS.DELETE')"
|
||||||
variant="link"
|
variant="link"
|
||||||
size="medium"
|
size="medium"
|
||||||
@@ -31,20 +35,25 @@
|
|||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="notAttributeTypeCheckboxAndList">
|
<div v-if="notAttributeTypeCheckboxAndList">
|
||||||
<div v-show="isEditing">
|
<div v-if="isEditing" v-on-clickaway="onClickAway">
|
||||||
<div class="input-group small">
|
<div class="mb-2 w-full flex items-center">
|
||||||
<input
|
<input
|
||||||
ref="inputfield"
|
ref="inputfield"
|
||||||
v-model="editedValue"
|
v-model="editedValue"
|
||||||
:type="inputType"
|
:type="inputType"
|
||||||
class="input-group-field"
|
class="!h-8 ltr:rounded-r-none rtl:rounded-l-none !mb-0 !text-sm"
|
||||||
autofocus="true"
|
autofocus="true"
|
||||||
:class="{ error: $v.editedValue.$error }"
|
:class="{ error: $v.editedValue.$error }"
|
||||||
@blur="$v.editedValue.$touch"
|
@blur="$v.editedValue.$touch"
|
||||||
@keyup.enter="onUpdate"
|
@keyup.enter="onUpdate"
|
||||||
/>
|
/>
|
||||||
<div class="input-group-button">
|
<div>
|
||||||
<woot-button size="small" icon="checkmark" @click="onUpdate" />
|
<woot-button
|
||||||
|
size="small"
|
||||||
|
icon="checkmark"
|
||||||
|
class="rounded-l-none rtl:rounded-r-none"
|
||||||
|
@click="onUpdate"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
@@ -56,7 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-show="!isEditing"
|
v-show="!isEditing"
|
||||||
class="value--view"
|
class="flex group"
|
||||||
:class="{ 'is-editable': showActions }"
|
:class="{ 'is-editable': showActions }"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
@@ -64,35 +73,35 @@
|
|||||||
:href="hrefURL"
|
:href="hrefURL"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="value inline-block rounded-sm mb-0 break-all py-0.5 px-1"
|
class="group-hover:bg-slate-50 group-hover:dark:bg-slate-700 inline-block rounded-sm mb-0 break-all py-0.5 px-1"
|
||||||
>
|
>
|
||||||
{{ urlValue }}
|
{{ urlValue }}
|
||||||
</a>
|
</a>
|
||||||
<p
|
<p
|
||||||
v-else
|
v-else
|
||||||
class="value inline-block rounded-sm mb-0 break-all py-0.5 px-1"
|
class="group-hover:bg-slate-50 group-hover:dark:bg-slate-700 inline-block rounded-sm mb-0 break-all py-0.5 px-1"
|
||||||
>
|
>
|
||||||
{{ displayValue || '---' }}
|
{{ displayValue || '---' }}
|
||||||
</p>
|
</p>
|
||||||
<div class="flex max-w-[2rem] gap-1 ml-1 rtl:mr-1 rtl:ml-0">
|
<div class="flex max-w-[2rem] gap-1 ml-1 rtl:mr-1 rtl:ml-0">
|
||||||
<woot-button
|
<woot-button
|
||||||
v-if="showActions"
|
v-if="showCopyAndDeleteButton"
|
||||||
v-tooltip="$t('CUSTOM_ATTRIBUTES.ACTIONS.COPY')"
|
v-tooltip="$t('CUSTOM_ATTRIBUTES.ACTIONS.COPY')"
|
||||||
variant="link"
|
variant="link"
|
||||||
size="small"
|
size="small"
|
||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
icon="clipboard"
|
icon="clipboard"
|
||||||
class-names="edit-button"
|
class-names="hidden group-hover:flex !w-6 flex-shrink-0"
|
||||||
@click="onCopy"
|
@click="onCopy"
|
||||||
/>
|
/>
|
||||||
<woot-button
|
<woot-button
|
||||||
v-if="showActions"
|
v-if="showEditButton"
|
||||||
v-tooltip.right="$t('CUSTOM_ATTRIBUTES.ACTIONS.EDIT')"
|
v-tooltip.right="$t('CUSTOM_ATTRIBUTES.ACTIONS.EDIT')"
|
||||||
variant="link"
|
variant="link"
|
||||||
size="small"
|
size="small"
|
||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
icon="edit"
|
icon="edit"
|
||||||
class-names="edit-button"
|
class-names="hidden group-hover:flex !w-6 flex-shrink-0"
|
||||||
@click="onEdit"
|
@click="onEdit"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -121,6 +130,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
import { format, parseISO } from 'date-fns';
|
import { format, parseISO } from 'date-fns';
|
||||||
import { required, url } from 'vuelidate/lib/validators';
|
import { required, url } from 'vuelidate/lib/validators';
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
@@ -133,7 +143,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
MultiselectDropdown,
|
MultiselectDropdown,
|
||||||
},
|
},
|
||||||
mixins: [customAttributeMixin],
|
mixins: [customAttributeMixin, clickaway],
|
||||||
props: {
|
props: {
|
||||||
label: { type: String, required: true },
|
label: { type: String, required: true },
|
||||||
values: { type: Array, default: () => [] },
|
values: { type: Array, default: () => [] },
|
||||||
@@ -155,11 +165,18 @@ export default {
|
|||||||
editedValue: null,
|
editedValue: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
showCopyAndDeleteButton() {
|
||||||
|
return this.value && this.showActions;
|
||||||
|
},
|
||||||
|
showEditButton() {
|
||||||
|
return !this.value && this.showActions;
|
||||||
|
},
|
||||||
displayValue() {
|
displayValue() {
|
||||||
if (this.isAttributeTypeDate) {
|
if (this.isAttributeTypeDate) {
|
||||||
return new Date(this.value || new Date()).toLocaleDateString();
|
return this.value
|
||||||
|
? new Date(this.value || new Date()).toLocaleDateString()
|
||||||
|
: '';
|
||||||
}
|
}
|
||||||
if (this.isAttributeTypeCheckbox) {
|
if (this.isAttributeTypeCheckbox) {
|
||||||
return this.value === 'false' ? false : this.value;
|
return this.value === 'false' ? false : this.value;
|
||||||
@@ -225,6 +242,10 @@ export default {
|
|||||||
this.isEditing = false;
|
this.isEditing = false;
|
||||||
this.editedValue = this.formattedValue;
|
this.editedValue = this.formattedValue;
|
||||||
},
|
},
|
||||||
|
contactId() {
|
||||||
|
// Fix to solve validation not resetting when contactId changes in contact page
|
||||||
|
this.$v.$reset();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
validations() {
|
validations() {
|
||||||
@@ -263,6 +284,10 @@ export default {
|
|||||||
this.$refs.inputfield.focus();
|
this.$refs.inputfield.focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onClickAway() {
|
||||||
|
this.$v.$reset();
|
||||||
|
this.isEditing = false;
|
||||||
|
},
|
||||||
onEdit() {
|
onEdit() {
|
||||||
this.isEditing = true;
|
this.isEditing = true;
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
@@ -289,6 +314,7 @@ export default {
|
|||||||
},
|
},
|
||||||
onDelete() {
|
onDelete() {
|
||||||
this.isEditing = false;
|
this.isEditing = false;
|
||||||
|
this.$v.$reset();
|
||||||
this.$emit('delete', this.attributeKey);
|
this.$emit('delete', this.attributeKey);
|
||||||
},
|
},
|
||||||
onCopy() {
|
onCopy() {
|
||||||
@@ -299,36 +325,6 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.checkbox-wrap {
|
|
||||||
@apply flex items-center;
|
|
||||||
}
|
|
||||||
.checkbox {
|
|
||||||
@apply my-0 mr-2 ml-0;
|
|
||||||
}
|
|
||||||
.attribute-name {
|
|
||||||
@apply w-full text-slate-800 dark:text-slate-100;
|
|
||||||
&.error {
|
|
||||||
@apply text-red-400 dark:text-red-500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-button {
|
|
||||||
@apply hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value--view {
|
|
||||||
@apply flex;
|
|
||||||
|
|
||||||
&.is-editable:hover {
|
|
||||||
.value {
|
|
||||||
@apply bg-slate-50 dark:bg-slate-700 mb-0;
|
|
||||||
}
|
|
||||||
.edit-button {
|
|
||||||
@apply block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep {
|
::v-deep {
|
||||||
.selector-wrap {
|
.selector-wrap {
|
||||||
@apply m-0 top-1;
|
@apply m-0 top-1;
|
||||||
|
|||||||
@@ -7,7 +7,13 @@
|
|||||||
@mousedown="handleMouseDown"
|
@mousedown="handleMouseDown"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
:class="modalContainerClassName"
|
:class="{
|
||||||
|
'modal-container rtl:text-right shadow-md max-h-full overflow-auto relative bg-white dark:bg-slate-800 skip-context-menu': true,
|
||||||
|
'rounded-xl w-[37.5rem]': !fullWidth,
|
||||||
|
'items-center rounded-none flex h-full justify-center w-full':
|
||||||
|
fullWidth,
|
||||||
|
[size]: true,
|
||||||
|
}"
|
||||||
@mouse.stop
|
@mouse.stop
|
||||||
@mousedown="event => event.stopPropagation()"
|
@mousedown="event => event.stopPropagation()"
|
||||||
>
|
>
|
||||||
@@ -16,7 +22,7 @@
|
|||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
icon="dismiss"
|
icon="dismiss"
|
||||||
variant="clear"
|
variant="clear"
|
||||||
class="absolute ltr:right-2 rtl:left-2 top-2 z-10"
|
class="absolute z-10 ltr:right-2 rtl:left-2 top-2"
|
||||||
@click="close"
|
@click="close"
|
||||||
/>
|
/>
|
||||||
<slot />
|
<slot />
|
||||||
@@ -60,15 +66,6 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
modalContainerClassName() {
|
|
||||||
let className =
|
|
||||||
'modal-container bg-white dark:bg-slate-800 skip-context-menu';
|
|
||||||
if (this.fullWidth) {
|
|
||||||
return `${className} modal-container--full-width`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${className} ${this.size}`;
|
|
||||||
},
|
|
||||||
modalClassName() {
|
modalClassName() {
|
||||||
const modalClassNameMap = {
|
const modalClassNameMap = {
|
||||||
centered: '',
|
centered: '',
|
||||||
@@ -109,26 +106,43 @@ export default {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style lang="scss">
|
||||||
.modal-container--full-width {
|
.modal-mask {
|
||||||
align-items: center;
|
@apply flex items-center justify-center bg-modal-backdrop-light dark:bg-modal-backdrop-dark z-[9990] h-full left-0 fixed top-0 w-full;
|
||||||
border-radius: 0;
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-mask.right-aligned {
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
.modal-container {
|
.modal-container {
|
||||||
border-radius: 0;
|
&.medium {
|
||||||
height: 100%;
|
@apply max-w-[80%] w-[56.25rem];
|
||||||
width: 30rem;
|
}
|
||||||
|
// .content-box {
|
||||||
|
// @apply h-auto p-0;
|
||||||
|
// }
|
||||||
|
.content {
|
||||||
|
@apply p-8;
|
||||||
|
}
|
||||||
|
form,
|
||||||
|
.modal-content {
|
||||||
|
@apply pt-4 pb-8 px-8 self-center;
|
||||||
|
a {
|
||||||
|
@apply p-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modal-big {
|
.modal-big {
|
||||||
width: 60%;
|
@apply w-full;
|
||||||
|
}
|
||||||
|
.modal-mask.right-aligned {
|
||||||
|
@apply justify-end;
|
||||||
|
.modal-container {
|
||||||
|
@apply rounded-none h-full w-[30rem];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modal-enter,
|
||||||
|
.modal-leave {
|
||||||
|
@apply opacity-0;
|
||||||
|
}
|
||||||
|
.modal-enter .modal-container,
|
||||||
|
.modal-leave .modal-container {
|
||||||
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col items-start pt-8 px-8 pb-0">
|
<div class="flex flex-col items-start px-8 pt-8 pb-0">
|
||||||
<img v-if="headerImage" :src="headerImage" alt="No image" />
|
<img v-if="headerImage" :src="headerImage" alt="No image" />
|
||||||
<h2
|
<h2
|
||||||
ref="modalHeaderTitle"
|
ref="modalHeaderTitle"
|
||||||
class="text-slate-800 text-lg dark:text-slate-100"
|
class="text-base font-semibold leading-6 text-slate-800 dark:text-slate-50"
|
||||||
>
|
>
|
||||||
{{ headerTitle }}
|
{{ headerTitle }}
|
||||||
</h2>
|
</h2>
|
||||||
<p
|
<p
|
||||||
v-if="headerContent"
|
v-if="headerContent"
|
||||||
ref="modalHeaderContent"
|
ref="modalHeaderContent"
|
||||||
class="w-full break-words text-slate-600 mt-2 text-sm dark:text-slate-300"
|
class="w-full mt-2 text-sm leading-5 break-words text-slate-600 dark:text-slate-300"
|
||||||
>
|
>
|
||||||
{{ headerContent }}
|
{{ headerContent }}
|
||||||
<span
|
<span
|
||||||
v-if="headerContentValue"
|
v-if="headerContentValue"
|
||||||
class="font-semibold text-sm text-slate-600 dark:text-slate-300"
|
class="text-sm font-semibold text-slate-600 dark:text-slate-300"
|
||||||
>
|
>
|
||||||
{{ headerContentValue }}
|
{{ headerContentValue }}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="ml-0 mr-0 flex pt-0 pr-4 pb-4 pl-0"
|
class="ml-0 mr-0 flex py-8 w-full xl:w-3/4 flex-col xl:flex-row"
|
||||||
:class="{
|
:class="{
|
||||||
'pt-4 border-b border-solid border-slate-50 dark:border-slate-700/30':
|
'border-b border-solid border-slate-50 dark:border-slate-700/30':
|
||||||
showBorder,
|
showBorder,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="w-[30%] min-w-0 max-w-[30%] pr-12">
|
<div class="w-full xl:w-1/4 min-w-0 xl:max-w-[30%] pr-12">
|
||||||
<p
|
<p
|
||||||
v-if="title"
|
v-if="title"
|
||||||
class="text-base text-woot-500 dark:text-woot-500 mb-0 font-medium"
|
class="text-base text-woot-500 dark:text-woot-500 mb-0 font-medium"
|
||||||
>
|
>
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm mb-2">
|
<p
|
||||||
|
class="text-sm mb-2 text-slate-700 dark:text-slate-300 leading-5 tracking-normal mt-2"
|
||||||
|
>
|
||||||
<slot v-if="subTitle" name="subTitle">
|
<slot v-if="subTitle" name="subTitle">
|
||||||
{{ subTitle }}
|
{{ subTitle }}
|
||||||
</slot>
|
</slot>
|
||||||
@@ -23,7 +25,7 @@
|
|||||||
{{ note }}
|
{{ note }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-[50%] min-w-0 max-w-[50%]">
|
<div class="w-full xl:w-1/2 min-w-0 xl:max-w-[50%]">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="ui-snackbar">
|
<div
|
||||||
<div class="ui-snackbar-text">
|
class="shadow-sm bg-slate-800 dark:bg-slate-700 rounded-[4px] items-center gap-3 inline-flex mb-2 max-w-[25rem] min-h-[1.875rem] min-w-[15rem] px-6 py-3 text-left"
|
||||||
|
>
|
||||||
|
<div class="text-white dark:text-white text-sm font-medium">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="action" class="ui-snackbar-action">
|
<div v-if="action">
|
||||||
<router-link v-if="action.type == 'link'" :to="action.to">
|
<router-link
|
||||||
|
v-if="action.type == 'link'"
|
||||||
|
:to="action.to"
|
||||||
|
class="text-woot-500 dark:text-woot-500 cursor-pointer font-medium hover:text-woot-600 dark:hover:text-woot-600 select-none"
|
||||||
|
>
|
||||||
{{ action.message }}
|
{{ action.message }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition-group name="toast-fade" tag="div" class="ui-snackbar-container">
|
<transition-group
|
||||||
|
name="toast-fade"
|
||||||
|
tag="div"
|
||||||
|
class="left-0 my-0 mx-auto max-w-[25rem] overflow-hidden absolute right-0 text-center top-4 z-[9999]"
|
||||||
|
>
|
||||||
<woot-snackbar
|
<woot-snackbar
|
||||||
v-for="snackMessage in snackMessages"
|
v-for="snackMessage in snackMessages"
|
||||||
:key="snackMessage.key"
|
:key="snackMessage.key"
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
v-on-clickaway="closeDropdown"
|
v-on-clickaway="closeDropdown"
|
||||||
class="dropdown-pane dropdown-pane--open"
|
class="dropdown-pane dropdown-pane--open"
|
||||||
>
|
>
|
||||||
<woot-dropdown-menu>
|
<woot-dropdown-menu class="mb-0">
|
||||||
<woot-dropdown-item v-if="!isPending">
|
<woot-dropdown-item v-if="!isPending">
|
||||||
<woot-button
|
<woot-button
|
||||||
variant="clear"
|
variant="clear"
|
||||||
@@ -91,12 +91,7 @@ import { mapGetters } from 'vuex';
|
|||||||
import { mixin as clickaway } from 'vue-clickaway';
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue';
|
import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue';
|
||||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
||||||
import {
|
|
||||||
hasPressedAltAndEKey,
|
|
||||||
hasPressedCommandPlusAltAndEKey,
|
|
||||||
hasPressedAltAndMKey,
|
|
||||||
} from 'shared/helpers/KeyboardHelpers';
|
|
||||||
import { findSnoozeTime } from 'dashboard/helper/snoozeHelpers';
|
import { findSnoozeTime } from 'dashboard/helper/snoozeHelpers';
|
||||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
||||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
||||||
@@ -114,7 +109,7 @@ export default {
|
|||||||
WootDropdownMenu,
|
WootDropdownMenu,
|
||||||
CustomSnoozeModal,
|
CustomSnoozeModal,
|
||||||
},
|
},
|
||||||
mixins: [clickaway, alertMixin, eventListenerMixins],
|
mixins: [clickaway, alertMixin, keyboardEventListenerMixins],
|
||||||
props: { conversationId: { type: [String, Number], required: true } },
|
props: { conversationId: { type: [String, Number], required: true } },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -159,37 +154,52 @@ export default {
|
|||||||
bus.$off(CMD_RESOLVE_CONVERSATION, this.onCmdResolveConversation);
|
bus.$off(CMD_RESOLVE_CONVERSATION, this.onCmdResolveConversation);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async handleKeyEvents(e) {
|
getKeyboardEvents() {
|
||||||
|
return {
|
||||||
|
'Alt+KeyM': {
|
||||||
|
action: () => this.$refs.arrowDownButton?.$el.click(),
|
||||||
|
allowOnFocusedInput: true,
|
||||||
|
},
|
||||||
|
'Alt+KeyE': this.resolveOrToast,
|
||||||
|
'$mod+Alt+KeyE': async event => {
|
||||||
|
const { all, activeIndex, lastIndex } = this.getConversationParams();
|
||||||
|
await this.resolveOrToast();
|
||||||
|
|
||||||
|
if (activeIndex < lastIndex) {
|
||||||
|
all[activeIndex + 1].click();
|
||||||
|
} else if (all.length > 1) {
|
||||||
|
all[0].click();
|
||||||
|
document.querySelector('.conversations-list').scrollTop = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getConversationParams() {
|
||||||
const allConversations = document.querySelectorAll(
|
const allConversations = document.querySelectorAll(
|
||||||
'.conversations-list .conversation'
|
'.conversations-list .conversation'
|
||||||
);
|
);
|
||||||
if (hasPressedAltAndMKey(e)) {
|
|
||||||
if (this.$refs.arrowDownButton) {
|
const activeConversation = document.querySelector(
|
||||||
this.$refs.arrowDownButton.$el.click();
|
'div.conversations-list div.conversation.active'
|
||||||
}
|
);
|
||||||
}
|
const activeConversationIndex = [...allConversations].indexOf(
|
||||||
if (hasPressedAltAndEKey(e)) {
|
activeConversation
|
||||||
const activeConversation = document.querySelector(
|
);
|
||||||
'div.conversations-list div.conversation.active'
|
const lastConversationIndex = allConversations.length - 1;
|
||||||
);
|
|
||||||
const activeConversationIndex = [...allConversations].indexOf(
|
return {
|
||||||
activeConversation
|
all: allConversations,
|
||||||
);
|
activeIndex: activeConversationIndex,
|
||||||
const lastConversationIndex = allConversations.length - 1;
|
lastIndex: lastConversationIndex,
|
||||||
try {
|
};
|
||||||
await this.toggleStatus(wootConstants.STATUS_TYPE.RESOLVED);
|
},
|
||||||
} catch (error) {
|
async resolveOrToast() {
|
||||||
// error
|
try {
|
||||||
}
|
await this.toggleStatus(wootConstants.STATUS_TYPE.RESOLVED);
|
||||||
if (hasPressedCommandPlusAltAndEKey(e)) {
|
} catch (error) {
|
||||||
if (activeConversationIndex < lastConversationIndex) {
|
// error
|
||||||
allConversations[activeConversationIndex + 1].click();
|
|
||||||
} else if (allConversations.length > 1) {
|
|
||||||
allConversations[0].click();
|
|
||||||
document.querySelector('.conversations-list').scrollTop = 0;
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onCmdSnoozeConversation(snoozeType) {
|
onCmdSnoozeConversation(snoozeType) {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import Label from './ui/Label';
|
|||||||
import LoadingState from './widgets/LoadingState';
|
import LoadingState from './widgets/LoadingState';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import ModalHeader from './ModalHeader';
|
import ModalHeader from './ModalHeader';
|
||||||
import ReportStatsCard from './widgets/ReportStatsCard';
|
|
||||||
import SidemenuIcon from './SidemenuIcon';
|
import SidemenuIcon from './SidemenuIcon';
|
||||||
import Spinner from 'shared/components/Spinner';
|
import Spinner from 'shared/components/Spinner';
|
||||||
import SubmitButton from './buttons/FormSubmitButton';
|
import SubmitButton from './buttons/FormSubmitButton';
|
||||||
@@ -46,7 +45,6 @@ const WootUIKit = {
|
|||||||
LoadingState,
|
LoadingState,
|
||||||
Modal,
|
Modal,
|
||||||
ModalHeader,
|
ModalHeader,
|
||||||
ReportStatsCard,
|
|
||||||
SidemenuIcon,
|
SidemenuIcon,
|
||||||
Spinner,
|
Spinner,
|
||||||
SubmitButton,
|
SubmitButton,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user