diff --git a/.circleci/config.yml b/.circleci/config.yml index e2c6dc583..c67063ae6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,9 +15,9 @@ defaults: &defaults - image: cimg/postgres:15.3 - image: cimg/redis:6.2.6 environment: - - RAILS_LOG_TO_STDOUT: false - - COVERAGE: true - - LOG_LEVEL: warn + - RAILS_LOG_TO_STDOUT: false + - COVERAGE: true + - LOG_LEVEL: warn parallelism: 4 resource_class: large @@ -46,7 +46,7 @@ jobs: export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" - nvm install v16 + nvm install v20 echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV @@ -64,7 +64,6 @@ jobs: - ~/.bundle key: chatwoot-bundle-{{ .Environment.CACHE_VERSION }}-v20220524-{{ checksum "Gemfile.lock" }} - # Only necessary if app uses webpacker or yarn in some other way - restore_cache: keys: @@ -82,7 +81,7 @@ jobs: - ~/.cache/yarn - run: - name: Download cc-test-reporter + name: Download cc-test-reporter command: | mkdir -p ~/tmp curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ~/tmp/cc-test-reporter @@ -182,7 +181,7 @@ jobs: - attach_workspace: at: ~/build - run: - name: Download cc-test-reporter + name: Download cc-test-reporter command: | mkdir -p ~/tmp curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ~/tmp/cc-test-reporter @@ -204,4 +203,4 @@ workflows: - build - upload-coverage: requires: - - build + - build diff --git a/.env.example b/.env.example index a3a5bf7cc..efee18735 100644 --- a/.env.example +++ b/.env.example @@ -199,6 +199,8 @@ ANDROID_SHA256_CERT_FINGERPRINT=AC:73:8E:DE:EB:56:EA:CC:10:87:02:A7:65:37:7B:38: ## Rack Attack configuration ## To prevent and throttle abusive requests # ENABLE_RACK_ATTACK=true +# RACK_ATTACK_IP_LIMIT=3000 +# ENABLE_RACK_ATTACK_WIDGET_API=true ## Running chatwoot as an API only server ## setting this value to true will disable the frontend dashboard endpoints diff --git a/.github/workflows/run_foss_spec.yml b/.github/workflows/run_foss_spec.yml index 71009f5a0..7a32f3fd1 100644 --- a/.github/workflows/run_foss_spec.yml +++ b/.github/workflows/run_foss_spec.yml @@ -52,7 +52,8 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 20 + cache: yarn - name: yarn run: yarn install @@ -75,6 +76,8 @@ jobs: - name: Run backend tests run: | bundle exec rspec --profile=10 --format documentation + env: + NODE_OPTIONS: --openssl-legacy-provider - name: Upload rails log folder uses: actions/upload-artifact@v3 diff --git a/.github/workflows/run_response_bot_spec.yml b/.github/workflows/run_response_bot_spec.yml index daa89494a..ded5c903e 100644 --- a/.github/workflows/run_response_bot_spec.yml +++ b/.github/workflows/run_response_bot_spec.yml @@ -51,7 +51,8 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 20 + cache: yarn - name: yarn run: yarn install diff --git a/.github/workflows/size-limit.yml b/.github/workflows/size-limit.yml index efe497157..01d72339d 100644 --- a/.github/workflows/size-limit.yml +++ b/.github/workflows/size-limit.yml @@ -21,7 +21,8 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 20 + cache: 'yarn' - name: yarn run: yarn install @@ -30,9 +31,11 @@ jobs: run: | rm -rf enterprise rm -rf spec/enterprise - + - name: Run asset compile run: bundle exec rake assets:precompile + env: + NODE_OPTIONS: --openssl-legacy-provider - name: Size Check run: yarn run size diff --git a/.nvmrc b/.nvmrc index 47979412e..6f7af3750 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16.20.1 +20.5.1 \ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml index 2befbac4b..22e59629d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,15 +8,11 @@ Layout/LineLength: Max: 150 Metrics/ClassLength: - Max: 125 + Max: 175 Exclude: - - 'app/models/conversation.rb' - - 'app/models/contact.rb' - - 'app/mailers/conversation_reply_mailer.rb' - 'app/models/message.rb' - - 'app/builders/messages/facebook/message_builder.rb' - - 'app/controllers/api/v1/accounts/contacts_controller.rb' - - 'app/listeners/action_cable_listener.rb' + - 'app/models/conversation.rb' + RSpec/ExampleLength: Max: 25 Style/Documentation: @@ -73,7 +69,7 @@ Rails/ApplicationController: - 'app/controllers/survey/responses_controller.rb' Rails/FindEach: Enabled: true - Include: + Include: - 'app/**/*.rb' Rails/CompactBlank: Enabled: false @@ -189,7 +185,6 @@ RSpec/IndexedLet: RSpec/NamedSubject: Enabled: false - # we should bring this down RSpec/MultipleMemoizedHelpers: Max: 14 diff --git a/Gemfile b/Gemfile index 7e79d2e00..a6679f069 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ ruby '3.2.2' ##-- base gems for rails --## gem 'rack-cors', require: 'rack/cors' -gem 'rails', '~> 7.0.5.1' +gem 'rails', '~> 7.0.8.0' # Reduces boot times through caching; required in config/boot.rb gem 'bootsnap', require: false @@ -108,14 +108,14 @@ gem 'elastic-apm', require: false gem 'newrelic_rpm', require: false gem 'newrelic-sidekiq-metrics', require: false gem 'scout_apm', require: false -gem 'sentry-rails', '>= 5.10.0', require: false +gem 'sentry-rails', '>= 5.11.0', require: false gem 'sentry-ruby', require: false -gem 'sentry-sidekiq', '>= 5.10.0', require: false +gem 'sentry-sidekiq', '>= 5.11.0', require: false ##-- background job processing --## -gem 'sidekiq' +gem 'sidekiq', '>= 7.1.3' # We want cron jobs -gem 'sidekiq-cron' +gem 'sidekiq-cron', '>= 1.10.1' ##-- Push notification service --## gem 'fcm' @@ -188,7 +188,7 @@ group :development do gem 'bullet' gem 'letter_opener' gem 'scss_lint', require: false - gem 'web-console' + gem 'web-console', '>= 4.2.1' # used in swagger build gem 'json_refs' diff --git a/Gemfile.lock b/Gemfile.lock index f2b3ea86a..bf109b551 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -33,70 +33,70 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (7.0.5.1) - actionpack (= 7.0.5.1) - activesupport (= 7.0.5.1) + actioncable (7.0.8) + actionpack (= 7.0.8) + activesupport (= 7.0.8) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.5.1) - actionpack (= 7.0.5.1) - activejob (= 7.0.5.1) - activerecord (= 7.0.5.1) - activestorage (= 7.0.5.1) - activesupport (= 7.0.5.1) + actionmailbox (7.0.8) + actionpack (= 7.0.8) + activejob (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.5.1) - actionpack (= 7.0.5.1) - actionview (= 7.0.5.1) - activejob (= 7.0.5.1) - activesupport (= 7.0.5.1) + actionmailer (7.0.8) + actionpack (= 7.0.8) + actionview (= 7.0.8) + activejob (= 7.0.8) + activesupport (= 7.0.8) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.5.1) - actionview (= 7.0.5.1) - activesupport (= 7.0.5.1) + actionpack (7.0.8) + actionview (= 7.0.8) + activesupport (= 7.0.8) rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.5.1) - actionpack (= 7.0.5.1) - activerecord (= 7.0.5.1) - activestorage (= 7.0.5.1) - activesupport (= 7.0.5.1) + actiontext (7.0.8) + actionpack (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.5.1) - activesupport (= 7.0.5.1) + actionview (7.0.8) + activesupport (= 7.0.8) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) active_record_query_trace (1.8) - activejob (7.0.5.1) - activesupport (= 7.0.5.1) + activejob (7.0.8) + activesupport (= 7.0.8) globalid (>= 0.3.6) - activemodel (7.0.5.1) - activesupport (= 7.0.5.1) - activerecord (7.0.5.1) - activemodel (= 7.0.5.1) - activesupport (= 7.0.5.1) + activemodel (7.0.8) + activesupport (= 7.0.8) + activerecord (7.0.8) + activemodel (= 7.0.8) + activesupport (= 7.0.8) activerecord-import (1.4.1) activerecord (>= 4.2) - activestorage (7.0.5.1) - actionpack (= 7.0.5.1) - activejob (= 7.0.5.1) - activerecord (= 7.0.5.1) - activesupport (= 7.0.5.1) + activestorage (7.0.8) + actionpack (= 7.0.8) + activejob (= 7.0.8) + activerecord (= 7.0.8) + activesupport (= 7.0.8) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.5.1) + activesupport (7.0.8) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -269,8 +269,8 @@ GEM grpc (~> 1.36) geocoder (1.8.1) gli (2.21.0) - globalid (1.1.0) - activesupport (>= 5.0) + globalid (1.2.1) + activesupport (>= 6.1) gmail_xoauth (0.4.2) oauth (>= 0.3.6) google-apis-core (0.11.0) @@ -450,9 +450,9 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2023.0218.1) mini_magick (4.12.0) - mini_mime (1.1.2) + mini_mime (1.1.5) mini_portile2 (2.8.4) - minitest (5.19.0) + minitest (5.20.0) mock_redis (0.36.0) ruby2_keywords msgpack (1.7.0) @@ -463,7 +463,7 @@ GEM activerecord (>= 5.2) net-http-persistent (4.0.2) connection_pool (~> 2.2) - net-imap (0.3.6) + net-imap (0.3.7) date net-protocol net-pop (0.1.2) @@ -478,14 +478,14 @@ GEM sidekiq newrelic_rpm (8.16.0) nio4r (2.5.9) - nokogiri (1.15.3) + nokogiri (1.15.4) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.15.3-arm64-darwin) + nokogiri (1.15.4-arm64-darwin) racc (~> 1.4) - nokogiri (1.15.3-x86_64-darwin) + nokogiri (1.15.4-x86_64-darwin) racc (~> 1.4) - nokogiri (1.15.3-x86_64-linux) + nokogiri (1.15.4-x86_64-linux) racc (~> 1.4) numo-narray (0.9.2.1) oauth (1.1.0) @@ -543,7 +543,7 @@ GEM pry-rails (0.3.9) pry (>= 0.10.4) public_suffix (5.0.1) - puma (6.2.2) + puma (6.3.1) nio4r (~> 2.0) pundit (2.3.0) activesupport (>= 3.0.0) @@ -563,30 +563,30 @@ GEM rack-test (2.1.0) rack (>= 1.3) rack-timeout (0.6.3) - rails (7.0.5.1) - actioncable (= 7.0.5.1) - actionmailbox (= 7.0.5.1) - actionmailer (= 7.0.5.1) - actionpack (= 7.0.5.1) - actiontext (= 7.0.5.1) - actionview (= 7.0.5.1) - activejob (= 7.0.5.1) - activemodel (= 7.0.5.1) - activerecord (= 7.0.5.1) - activestorage (= 7.0.5.1) - activesupport (= 7.0.5.1) + rails (7.0.8) + actioncable (= 7.0.8) + actionmailbox (= 7.0.8) + actionmailer (= 7.0.8) + actionpack (= 7.0.8) + actiontext (= 7.0.8) + actionview (= 7.0.8) + activejob (= 7.0.8) + activemodel (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) bundler (>= 1.15.0) - railties (= 7.0.5.1) - rails-dom-testing (2.1.1) + railties (= 7.0.8) + rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (7.0.5.1) - actionpack (= 7.0.5.1) - activesupport (= 7.0.5.1) + railties (7.0.8) + actionpack (= 7.0.8) + activesupport (= 7.0.8) method_source rake (>= 12.2) thor (~> 1.0) @@ -598,7 +598,7 @@ GEM ffi (~> 1.0) redis (5.0.6) redis-client (>= 0.9.0) - redis-client (0.14.1) + redis-client (0.17.0) connection_pool redis-namespace (1.10.0) redis (>= 4) @@ -697,23 +697,23 @@ GEM activesupport (>= 4) selectize-rails (0.12.6) semantic_range (3.0.0) - sentry-rails (5.10.0) + sentry-rails (5.11.0) railties (>= 5.0) - sentry-ruby (~> 5.10.0) - sentry-ruby (5.10.0) + sentry-ruby (~> 5.11.0) + sentry-ruby (5.11.0) concurrent-ruby (~> 1.0, >= 1.0.2) - sentry-sidekiq (5.10.0) - sentry-ruby (~> 5.10.0) + sentry-sidekiq (5.11.0) + sentry-ruby (~> 5.11.0) sidekiq (>= 3.0) sexp_processor (4.17.0) shoulda-matchers (5.3.0) activesupport (>= 5.2.0) - sidekiq (7.1.2) + sidekiq (7.1.3) concurrent-ruby (< 2) connection_pool (>= 2.3.0) rack (>= 2.2.4) redis-client (>= 0.14.0) - sidekiq-cron (1.10.0) + sidekiq-cron (1.10.1) fugit (~> 1.8) globalid (>= 1.0.1) sidekiq (>= 6) @@ -786,7 +786,7 @@ GEM version_gem (1.1.2) warden (1.2.9) rack (>= 2.0.9) - web-console (4.2.0) + web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) @@ -805,14 +805,14 @@ GEM railties (>= 5.2) semantic_range (>= 2.3.0) webrick (1.8.1) - websocket-driver (0.7.5) + websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) wisper (2.0.0) working_hours (1.4.1) activesupport (>= 3.2) tzinfo - zeitwerk (2.6.9) + zeitwerk (2.6.11) PLATFORMS arm64-darwin-20 @@ -907,7 +907,7 @@ DEPENDENCIES rack-cors rack-mini-profiler (>= 3.1.1) rack-timeout - rails (~> 7.0.5.1) + rails (~> 7.0.8.0) redis redis-namespace responders @@ -922,12 +922,12 @@ DEPENDENCIES scout_apm scss_lint seed_dump - sentry-rails (>= 5.10.0) + sentry-rails (>= 5.11.0) sentry-ruby - sentry-sidekiq (>= 5.10.0) + sentry-sidekiq (>= 5.11.0) shoulda-matchers - sidekiq - sidekiq-cron + sidekiq (>= 7.1.3) + sidekiq-cron (>= 1.10.1) simplecov (= 0.17.1) slack-ruby-client (~> 2.0.0) spring @@ -943,7 +943,7 @@ DEPENDENCIES tzinfo-data uglifier valid_email2 - web-console + web-console (>= 4.2.1) web-push webmock webpacker diff --git a/Procfile.dev b/Procfile.dev index 94371cbae..73c2af7e8 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,4 +1,4 @@ backend: bin/rails s -p 3000 -frontend: bin/webpack-dev-server +frontend: export NODE_OPTIONS=--openssl-legacy-provider && bin/webpack-dev-server # https://github.com/mperham/sidekiq/issues/3090#issuecomment-389748695 worker: dotenv bundle exec sidekiq -C config/sidekiq.yml diff --git a/app.json b/app.json index 6324672fb..a89f2a08a 100644 --- a/app.json +++ b/app.json @@ -60,6 +60,9 @@ ], "stack": "heroku-20", "buildpacks": [ + { + "url": "heroku/nodejs" + }, { "url": "heroku/ruby" } diff --git a/app/assets/stylesheets/administrate/application.scss b/app/assets/stylesheets/administrate/application.scss index 86c5254ec..b8c5df794 100644 --- a/app/assets/stylesheets/administrate/application.scss +++ b/app/assets/stylesheets/administrate/application.scss @@ -29,3 +29,5 @@ @import 'components/pagination'; @import 'components/search'; @import 'components/reports'; + +@import 'custom_styles'; diff --git a/app/assets/stylesheets/administrate/custom_styles.scss b/app/assets/stylesheets/administrate/custom_styles.scss new file mode 100644 index 000000000..859a35689 --- /dev/null +++ b/app/assets/stylesheets/administrate/custom_styles.scss @@ -0,0 +1,24 @@ +// custom styles for the dashboard + +.feature-cell { + background: $color-extra-light-blue; + border-radius: 10px; + float: left; + margin-left: 8px; + margin-top: 6px; + padding: 4px 12px; + + .icon-container { + margin-right: 4px; + + } + + .value-container { + margin-left: 6px; + } +} + + +.feature-container { + max-width: 100rem; +} diff --git a/app/controllers/api/v1/accounts/articles_controller.rb b/app/controllers/api/v1/accounts/articles_controller.rb index a1e348723..7762cc476 100644 --- a/app/controllers/api/v1/accounts/articles_controller.rb +++ b/app/controllers/api/v1/accounts/articles_controller.rb @@ -1,7 +1,7 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController before_action :portal before_action :check_authorization - before_action :fetch_article, except: [:index, :create, :attach_file, :reorder] + before_action :fetch_article, except: [:index, :create, :reorder] before_action :set_current_page, only: [:index] def index @@ -36,17 +36,6 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController head :ok end - def attach_file - file_blob = ActiveStorage::Blob.create_and_upload!( - key: nil, - io: params[:background_image].tempfile, - filename: params[:background_image].original_filename, - content_type: params[:background_image].content_type - ) - file_blob.save! - render json: { file_url: url_for(file_blob) } - end - def reorder Article.update_positions(params[:positions_hash]) head :ok diff --git a/app/controllers/api/v1/accounts/automation_rules_controller.rb b/app/controllers/api/v1/accounts/automation_rules_controller.rb index 3431af9c3..3d894808d 100644 --- a/app/controllers/api/v1/accounts/automation_rules_controller.rb +++ b/app/controllers/api/v1/accounts/automation_rules_controller.rb @@ -20,16 +20,6 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont @automation_rule end - def attach_file - file_blob = ActiveStorage::Blob.create_and_upload!( - key: nil, - io: params[:attachment].tempfile, - filename: params[:attachment].original_filename, - content_type: params[:attachment].content_type - ) - render json: { blob_key: file_blob.key, blob_id: file_blob.id } - end - def update ActiveRecord::Base.transaction do automation_rule_update diff --git a/app/controllers/api/v1/accounts/contacts_controller.rb b/app/controllers/api/v1/accounts/contacts_controller.rb index 587e082ce..d75dc71b5 100644 --- a/app/controllers/api/v1/accounts/contacts_controller.rb +++ b/app/controllers/api/v1/accounts/contacts_controller.rb @@ -18,7 +18,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController def index @contacts_count = resolved_contacts.count - @contacts = fetch_contacts_with_conversation_count(resolved_contacts) + @contacts = fetch_contacts(resolved_contacts) end def search @@ -29,7 +29,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController search: "%#{params[:q].strip}%" ) @contacts_count = contacts.count - @contacts = fetch_contacts_with_conversation_count(contacts) + @contacts = fetch_contacts(contacts) end def import @@ -63,7 +63,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController result = ::Contacts::FilterService.new(params.permit!, current_user).perform contacts = result[:contacts] @contacts_count = result[:count] - @contacts = fetch_contacts_with_conversation_count(contacts) + @contacts = fetch_contacts(contacts) end def contactable_inboxes @@ -125,17 +125,14 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController @current_page = params[:page] || 1 end - def fetch_contacts_with_conversation_count(contacts) - conversation_count_sub_query = 'SELECT COUNT(*) FROM "conversations" WHERE "conversations"."contact_id" = "contacts"."id"' - contacts_with_conversation_count = filtrate(contacts) - .select("contacts.*, (#{conversation_count_sub_query}) as conversations_count") - .group('contacts.id') - .includes([{ avatar_attachment: [:blob] }]) - .page(@current_page).per(RESULTS_PER_PAGE) + def fetch_contacts(contacts) + contacts_with_avatar = filtrate(contacts) + .includes([{ avatar_attachment: [:blob] }]) + .page(@current_page).per(RESULTS_PER_PAGE) - return contacts_with_conversation_count.includes([{ contact_inboxes: [:inbox] }]) if @include_contact_inboxes + return contacts_with_avatar.includes([{ contact_inboxes: [:inbox] }]) if @include_contact_inboxes - contacts_with_conversation_count + contacts_with_avatar end def build_contact_inbox diff --git a/app/controllers/api/v1/accounts/macros_controller.rb b/app/controllers/api/v1/accounts/macros_controller.rb index 604e053f1..5dcdd2023 100644 --- a/app/controllers/api/v1/accounts/macros_controller.rb +++ b/app/controllers/api/v1/accounts/macros_controller.rb @@ -39,16 +39,6 @@ class Api::V1::Accounts::MacrosController < Api::V1::Accounts::BaseController head :ok end - def attach_file - file_blob = ActiveStorage::Blob.create_and_upload!( - key: nil, - io: params[:attachment].tempfile, - filename: params[:attachment].original_filename, - content_type: params[:attachment].content_type - ) - render json: { blob_key: file_blob.key, blob_id: file_blob.id } - end - def execute ::MacrosExecutionJob.perform_later(@macro, conversation_ids: params[:conversation_ids], user: Current.user) diff --git a/app/controllers/api/v1/accounts/portals_controller.rb b/app/controllers/api/v1/accounts/portals_controller.rb index fcc02c6ec..6d2a181f0 100644 --- a/app/controllers/api/v1/accounts/portals_controller.rb +++ b/app/controllers/api/v1/accounts/portals_controller.rb @@ -1,7 +1,7 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController include ::FileTypeHelper - before_action :fetch_portal, except: [:index, :create, :attach_file] + before_action :fetch_portal, except: [:index, :create] before_action :check_authorization before_action :set_current_page, only: [:index] @@ -53,16 +53,6 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController @portal.logo.attach(blob) end - def attach_file - file_blob = ActiveStorage::Blob.create_and_upload!( - key: nil, - io: params[:logo].tempfile, - filename: params[:logo].original_filename, - content_type: params[:logo].content_type - ) - render json: { blob_key: file_blob.key, blob_id: file_blob.id } - end - private def fetch_portal diff --git a/app/controllers/api/v1/upload_controller.rb b/app/controllers/api/v1/upload_controller.rb new file mode 100644 index 000000000..ad6b0b96c --- /dev/null +++ b/app/controllers/api/v1/upload_controller.rb @@ -0,0 +1,13 @@ +class Api::V1::UploadController < Api::BaseController + def create + file_blob = ActiveStorage::Blob.create_and_upload!( + key: nil, + io: params[:attachment].tempfile, + filename: params[:attachment].original_filename, + content_type: params[:attachment].content_type + ) + file_blob.save! + + render json: { file_url: url_for(file_blob), blob_key: file_blob.key, blob_id: file_blob.id } + end +end diff --git a/app/controllers/concerns/ensure_current_account_helper.rb b/app/controllers/concerns/ensure_current_account_helper.rb index eb781dbfe..5d02e96c8 100644 --- a/app/controllers/concerns/ensure_current_account_helper.rb +++ b/app/controllers/concerns/ensure_current_account_helper.rb @@ -8,7 +8,7 @@ module EnsureCurrentAccountHelper def ensure_current_account account = Account.find(params[:account_id]) - ensure_account_is_active?(account) + render_unauthorized('Account is suspended') and return unless account.active? if current_user account_accessible_for_user?(account) @@ -27,8 +27,4 @@ module EnsureCurrentAccountHelper def account_accessible_for_bot?(account) render_unauthorized('You are not authorized to access this account') unless @resource.agent_bot_inboxes.find_by(account_id: account.id) end - - def ensure_account_is_active?(account) - render_unauthorized('Account is suspended') unless account.active? - end end diff --git a/app/controllers/public/api/v1/portals/base_controller.rb b/app/controllers/public/api/v1/portals/base_controller.rb index 851b8c549..68619baf8 100644 --- a/app/controllers/public/api/v1/portals/base_controller.rb +++ b/app/controllers/public/api/v1/portals/base_controller.rb @@ -1,6 +1,7 @@ class Public::Api::V1::Portals::BaseController < PublicController before_action :show_plain_layout around_action :set_locale + after_action :allow_iframe_requests private @@ -39,4 +40,8 @@ class Public::Api::V1::Portals::BaseController < PublicController I18n.with_locale(@locale, &) end + + def allow_iframe_requests + response.headers.delete('X-Frame-Options') if @is_plain_layout_enabled + end end diff --git a/app/javascript/dashboard/App.vue b/app/javascript/dashboard/App.vue index 05888c5c7..b115f17b5 100644 --- a/app/javascript/dashboard/App.vue +++ b/app/javascript/dashboard/App.vue @@ -2,7 +2,7 @@
diff --git a/app/javascript/dashboard/api/automation.js b/app/javascript/dashboard/api/automation.js index eef39d12c..e83ece3d1 100644 --- a/app/javascript/dashboard/api/automation.js +++ b/app/javascript/dashboard/api/automation.js @@ -9,14 +9,6 @@ class AutomationsAPI extends ApiClient { clone(automationId) { return axios.post(`${this.url}/${automationId}/clone`); } - - attachment(file) { - return axios.post(`${this.url}/attach_file`, file, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); - } } export default new AutomationsAPI(); diff --git a/app/javascript/dashboard/api/helpCenter/articles.js b/app/javascript/dashboard/api/helpCenter/articles.js index 5423b1de2..fb21a0847 100644 --- a/app/javascript/dashboard/api/helpCenter/articles.js +++ b/app/javascript/dashboard/api/helpCenter/articles.js @@ -47,20 +47,6 @@ class ArticlesAPI extends PortalsAPI { return axios.delete(`${this.url}/${portalSlug}/articles/${articleId}`); } - uploadImage({ portalSlug, file }) { - let formData = new FormData(); - formData.append('background_image', file); - return axios.post( - `${this.url}/${portalSlug}/articles/attach_file`, - formData, - { - headers: { - 'Content-Type': 'multipart/form-data', - }, - } - ); - } - reorderArticles({ portalSlug, reorderedGroup, categorySlug }) { return axios.post(`${this.url}/${portalSlug}/articles/reorder`, { positions_hash: reorderedGroup, diff --git a/app/javascript/dashboard/assets/scss/_foundation-custom.scss b/app/javascript/dashboard/assets/scss/_foundation-custom.scss index 8230d1f45..f7d10e633 100644 --- a/app/javascript/dashboard/assets/scss/_foundation-custom.scss +++ b/app/javascript/dashboard/assets/scss/_foundation-custom.scss @@ -26,7 +26,7 @@ code { background: $color-background; border-radius: var(--border-radius-large); padding: $space-two; - @apply bg-slate-50 dark:bg-slate-600 text-slate-800 dark:text-slate-100; + @apply bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-100; } } diff --git a/app/javascript/dashboard/assets/scss/_layout.scss b/app/javascript/dashboard/assets/scss/_layout.scss index 952274321..594a14d95 100644 --- a/app/javascript/dashboard/assets/scss/_layout.scss +++ b/app/javascript/dashboard/assets/scss/_layout.scss @@ -9,87 +9,25 @@ body { } .app-wrapper { - @include full-height; - flex-grow: 0; - min-height: 0; - width: 100%; + @apply h-full flex-grow-0 min-h-0 w-full; + + .button--fixed-top { + @apply fixed ltr:right-2 rtl:left-2 top-2 flex flex-row; + } } .banner + .app-wrapper { + // Reduce the height of the dashboard to make room for the banner. + // And causing the top right green-action button to be pushed down when scrolling. + @apply h-[calc(100%-48px)]; + .button--fixed-top { - top: 5.6 * $space-one; + @apply top-14; } .off-canvas-content { .button--fixed-top { - top: $space-small; + @apply top-2; } } } - -is-closed .app-root { - @include flex; - flex-direction: column; -} - -.app-content { - @include flex; - @include full-height; - min-height: 0; - overflow: hidden; -} - -.view-box { - @include full-height; - @include space-between-column; - - height: 100vh; - margin: 0; -} - -.view-panel { - flex-direction: column; - margin: 0; - overflow-y: auto; - padding: $space-normal; -} - -.content-box { - overflow: auto; - padding: $space-normal; -} - -.back-button { - @include flex; - - align-items: center; - color: $color-woot; - cursor: pointer; - font-size: $font-size-default; - font-weight: $font-weight-normal; - margin-right: $space-normal; - - &::before { - font-size: $font-size-large; - margin-right: $space-small; - vertical-align: text-bottom; - } -} - -.button-spinner { - float: right; -} - -.no-items-error-message { - @include flex; - @include full-height; - - align-items: center; - flex-direction: column; - justify-content: center; - - img { - max-width: $space-mega; - padding: $space-one; - } -} diff --git a/app/javascript/dashboard/assets/scss/_rtl.scss b/app/javascript/dashboard/assets/scss/_rtl.scss index 14897b245..6f16a2af4 100644 --- a/app/javascript/dashboard/assets/scss/_rtl.scss +++ b/app/javascript/dashboard/assets/scss/_rtl.scss @@ -1,20 +1,6 @@ .app-rtl--wrapper { direction: rtl; - .header-section.back-button { - direction: initial; - margin-left: var(--space-normal); - margin-right: var(--space-smaller); - } - - // Settings header action button - .button--fixed-top { - left: $space-small; - position: fixed; - right: unset; - top: $space-small; - } - // Woot Tabs .tabs-title { &:first-child { diff --git a/app/javascript/dashboard/assets/scss/_utility-helpers.scss b/app/javascript/dashboard/assets/scss/_utility-helpers.scss index a8025ba66..99f0fe3eb 100644 --- a/app/javascript/dashboard/assets/scss/_utility-helpers.scss +++ b/app/javascript/dashboard/assets/scss/_utility-helpers.scss @@ -71,9 +71,3 @@ align-items: center; display: flex; } - -.button--fixed-top { - position: fixed; - right: var(--space-small); - top: var(--space-small); -} diff --git a/app/javascript/dashboard/assets/scss/widgets/_buttons.scss b/app/javascript/dashboard/assets/scss/widgets/_buttons.scss index ab41dafdf..bcdc6176f 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_buttons.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_buttons.scss @@ -1,5 +1,5 @@ .button { - @apply items-center inline-flex h-10 mb-0; + @apply items-center inline-flex h-10 mb-0 gap-2; .button__content { @apply w-full; @@ -14,17 +14,9 @@ @apply px-2 py-0; } - .icon--emoji + .button__content { - @apply pl-2 rtl:pr-2 rtl:pl-0; - } - - .icon--font + .button__content { - @apply pl-2 rtl:pr-2 rtl:pl-0; - } - // @TODDO - Remove after moving all buttons to woot-button .icon + .button__content { - @apply pl-2 w-auto rtl:pr-2 rtl:pl-0; + @apply w-auto; } &.expanded { @@ -157,18 +149,10 @@ // Sizes &.tiny { @apply h-6; - - .icon + .button__content { - @apply pl-1 rtl:pr-1 rtl:pl-0; - } } &.small { @apply h-8 pb-1 pt-1; - - .icon + .button__content { - @apply pl-1 rtl:pr-1 rtl:pl-0; - } } &.large { diff --git a/app/javascript/dashboard/assets/scss/widgets/_woot-tables.scss b/app/javascript/dashboard/assets/scss/widgets/_woot-tables.scss index 8d6581f82..e7a1adbca 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_woot-tables.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_woot-tables.scss @@ -76,13 +76,13 @@ table { .ve-pagination-goto { @apply text-slate-600 dark:text-slate-200; + + .ve-pagination-goto-input { + @apply bg-white dark:bg-slate-900 text-slate-600 dark:text-slate-200; + } } .ve-pagination-li { @apply bg-white dark:bg-slate-900 text-slate-600 dark:text-slate-200 border-slate-75 dark:border-slate-700; } - - .ve-pagination-goto-input { - @apply bg-white dark:bg-slate-900 text-slate-600 dark:text-slate-200; - } } diff --git a/app/javascript/dashboard/components/CustomAttribute.vue b/app/javascript/dashboard/components/CustomAttribute.vue index 81747862a..9b145c1f2 100644 --- a/app/javascript/dashboard/components/CustomAttribute.vue +++ b/app/javascript/dashboard/components/CustomAttribute.vue @@ -61,7 +61,7 @@ > diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/Primary.vue b/app/javascript/dashboard/components/layout/sidebarComponents/Primary.vue index d2a9db3af..e6b5d0585 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/Primary.vue +++ b/app/javascript/dashboard/components/layout/sidebarComponents/Primary.vue @@ -46,6 +46,7 @@ import AgentDetails from './AgentDetails'; import NotificationBell from './NotificationBell'; import wootConstants from 'dashboard/constants/globals'; import { frontendURL } from 'dashboard/helper/URLHelper'; +import { ACCOUNT_EVENTS } from '../../../helper/AnalyticsHelper/events'; export default { components: { @@ -99,6 +100,7 @@ export default { window.$chatwoot.toggle(); }, openNotificationPanel() { + this.$track(ACCOUNT_EVENTS.OPENED_NOTIFICATIONS); this.$emit('open-notification-panel'); }, }, diff --git a/app/javascript/dashboard/components/ui/Banner.vue b/app/javascript/dashboard/components/ui/Banner.vue index 8ffe410e2..5d680fe48 100644 --- a/app/javascript/dashboard/components/ui/Banner.vue +++ b/app/javascript/dashboard/components/ui/Banner.vue @@ -1,5 +1,8 @@