diff --git a/Gemfile b/Gemfile index 61cac17924c6b..8debc879fb16e 100644 --- a/Gemfile +++ b/Gemfile @@ -109,14 +109,14 @@ gem 'elastic-apm', require: false gem 'newrelic_rpm', require: false gem 'newrelic-sidekiq-metrics', '>= 1.6.2', require: false gem 'scout_apm', require: false -gem 'sentry-rails', '>= 5.13.0', require: false +gem 'sentry-rails', '>= 5.14.0', require: false gem 'sentry-ruby', require: false -gem 'sentry-sidekiq', '>= 5.13.0', require: false +gem 'sentry-sidekiq', '>= 5.14.0', require: false ##-- background job processing --## gem 'sidekiq', '>= 7.1.3' # We want cron jobs -gem 'sidekiq-cron', '>= 1.11.0' +gem 'sidekiq-cron', '>= 1.12.0' ##-- Push notification service --## gem 'fcm' @@ -162,7 +162,7 @@ gem 'omniauth-oauth2' gem 'audited', '~> 5.4', '>= 5.4.1' # need for google auth -gem 'omniauth' +gem 'omniauth', '>= 2.1.2' gem 'omniauth-google-oauth2' gem 'omniauth-rails_csrf_protection', '~> 1.0' @@ -198,7 +198,7 @@ group :development do gem 'squasher' # profiling - gem 'rack-mini-profiler', '>= 3.1.1', require: false + gem 'rack-mini-profiler', '>= 3.2.0', require: false gem 'stackprof' # Should install the associated chrome extension to view query logs gem 'meta_request' @@ -224,7 +224,7 @@ group :development, :test do gem 'byebug', platform: :mri gem 'climate_control' gem 'debug', '~> 1.8' - gem 'factory_bot_rails' + gem 'factory_bot_rails', '>= 6.4.2' gem 'listen' gem 'mock_redis' gem 'pry-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 0a0916d6f74d4..0469be6c93627 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -230,10 +230,10 @@ GEM facebook-messenger (2.0.1) httparty (~> 0.13, >= 0.13.7) rack (>= 1.4.5) - factory_bot (6.2.1) + factory_bot (6.4.2) activesupport (>= 5.0.0) - factory_bot_rails (6.2.0) - factory_bot (~> 6.2.0) + factory_bot_rails (6.4.2) + factory_bot (~> 6.4) railties (>= 5.0.0) faker (3.2.0) i18n (>= 1.8.11, < 2) @@ -511,7 +511,7 @@ GEM rack (>= 1.2, < 4) snaky_hash (~> 2.0) version_gem (~> 1.1) - omniauth (2.1.1) + omniauth (2.1.2) hashie (>= 3.4.6) rack (>= 2.2.3) rack-protection @@ -566,10 +566,10 @@ GEM rack (< 4) rack-cors (2.0.1) rack (>= 2.0.0) - rack-mini-profiler (3.1.1) + rack-mini-profiler (3.2.0) rack (>= 1.2.0) - rack-protection (3.0.6) - rack + rack-protection (3.1.0) + rack (~> 2.2, >= 2.2.4) rack-proxy (0.7.6) rack rack-test (2.1.0) @@ -610,7 +610,7 @@ GEM ffi (~> 1.0) redis (5.0.6) redis-client (>= 0.9.0) - redis-client (0.18.0) + redis-client (0.19.0) connection_pool redis-namespace (1.10.0) redis (>= 4) @@ -709,13 +709,13 @@ GEM activesupport (>= 4) selectize-rails (0.12.6) semantic_range (3.0.0) - sentry-rails (5.13.0) + sentry-rails (5.14.0) railties (>= 5.0) - sentry-ruby (~> 5.13.0) - sentry-ruby (5.13.0) + sentry-ruby (~> 5.14.0) + sentry-ruby (5.14.0) concurrent-ruby (~> 1.0, >= 1.0.2) - sentry-sidekiq (5.13.0) - sentry-ruby (~> 5.13.0) + sentry-sidekiq (5.14.0) + sentry-ruby (~> 5.14.0) sidekiq (>= 3.0) sexp_processor (4.17.0) shoulda-matchers (5.3.0) @@ -725,7 +725,7 @@ GEM connection_pool (>= 2.3.0) rack (>= 2.2.4) redis-client (>= 0.14.0) - sidekiq-cron (1.11.0) + sidekiq-cron (1.12.0) fugit (~> 1.8) globalid (>= 1.0.1) sidekiq (>= 6) @@ -870,7 +870,7 @@ DEPENDENCIES elastic-apm email_reply_trimmer facebook-messenger - factory_bot_rails + factory_bot_rails (>= 6.4.2) faker fcm flag_shih_tzu @@ -905,7 +905,7 @@ DEPENDENCIES neighbor newrelic-sidekiq-metrics (>= 1.6.2) newrelic_rpm - omniauth + omniauth (>= 2.1.2) omniauth-google-oauth2 omniauth-oauth2 omniauth-rails_csrf_protection (~> 1.0) @@ -918,7 +918,7 @@ DEPENDENCIES pundit rack-attack (>= 6.7.0) rack-cors - rack-mini-profiler (>= 3.1.1) + rack-mini-profiler (>= 3.2.0) rack-timeout rails (~> 7.0.8.0) redis @@ -935,12 +935,12 @@ DEPENDENCIES scout_apm scss_lint seed_dump - sentry-rails (>= 5.13.0) + sentry-rails (>= 5.14.0) sentry-ruby - sentry-sidekiq (>= 5.13.0) + sentry-sidekiq (>= 5.14.0) shoulda-matchers sidekiq (>= 7.1.3) - sidekiq-cron (>= 1.11.0) + sidekiq-cron (>= 1.12.0) simplecov (= 0.17.1) slack-ruby-client (~> 2.2.0) spring diff --git a/VERSION_CW b/VERSION_CW index fd2a01863fdd3..bea438e9ade77 100644 --- a/VERSION_CW +++ b/VERSION_CW @@ -1 +1 @@ -3.1.0 +3.3.1 diff --git a/VERSION_CWCTL b/VERSION_CWCTL index e70b4523ae7ff..24ba9a38de68d 100644 --- a/VERSION_CWCTL +++ b/VERSION_CWCTL @@ -1 +1 @@ -2.6.0 +2.7.0 diff --git a/app/assets/stylesheets/administrate/application.scss b/app/assets/stylesheets/administrate/application.scss index b8c5df794bd22..66ac42405eff3 100644 --- a/app/assets/stylesheets/administrate/application.scss +++ b/app/assets/stylesheets/administrate/application.scss @@ -25,7 +25,6 @@ @import 'components/flashes'; @import 'components/form-actions'; @import 'components/main-content'; -@import 'components/navigation'; @import 'components/pagination'; @import 'components/search'; @import 'components/reports'; diff --git a/app/assets/stylesheets/administrate/base/_layout.scss b/app/assets/stylesheets/administrate/base/_layout.scss index c4c081a82b03b..c2415122b3716 100644 --- a/app/assets/stylesheets/administrate/base/_layout.scss +++ b/app/assets/stylesheets/administrate/base/_layout.scss @@ -1,7 +1,7 @@ html { background-color: $color-white; box-sizing: border-box; - font-size: 10px; + font-size: 16px; -webkit-font-smoothing: antialiased; } diff --git a/app/assets/stylesheets/administrate/components/_attributes.scss b/app/assets/stylesheets/administrate/components/_attributes.scss index 2b2936650a1dd..af6c27ac3f7fb 100644 --- a/app/assets/stylesheets/administrate/components/_attributes.scss +++ b/app/assets/stylesheets/administrate/components/_attributes.scss @@ -16,8 +16,8 @@ .attribute-data { float: left; margin-bottom: $base-spacing; - margin-left: 2rem; - width: calc(84% - 1rem); + margin-left: 1.25rem; + width: calc(84% - 0.625rem); } .attribute--nested { diff --git a/app/assets/stylesheets/administrate/components/_field-unit.scss b/app/assets/stylesheets/administrate/components/_field-unit.scss index 856c1872c55f9..91b5153e42b88 100644 --- a/app/assets/stylesheets/administrate/components/_field-unit.scss +++ b/app/assets/stylesheets/administrate/components/_field-unit.scss @@ -9,22 +9,22 @@ .field-unit__label { float: left; - margin-left: 1rem; + margin-left: 0.625rem; text-align: right; - width: calc(15% - 1rem); + width: calc(15% - 0.625rem); } .field-unit__field { float: left; - margin-left: 2rem; - max-width: 50rem; + margin-left: 1.25rem; + max-width: 31.15rem; width: 100%; } .field-unit--nested { border: $base-border; margin-left: 7.5%; - max-width: 60rem; + max-width: 37.5rem; padding: $small-spacing; width: 100%; diff --git a/app/assets/stylesheets/administrate/components/_form-actions.scss b/app/assets/stylesheets/administrate/components/_form-actions.scss index d87d17435a567..05ec352f2b567 100644 --- a/app/assets/stylesheets/administrate/components/_form-actions.scss +++ b/app/assets/stylesheets/administrate/components/_form-actions.scss @@ -1,3 +1,3 @@ .form-actions { - margin-left: calc(15% + 2rem); + margin-left: calc(15% + 1.25rem); } diff --git a/app/assets/stylesheets/administrate/components/_main-content.scss b/app/assets/stylesheets/administrate/components/_main-content.scss index 590bb098565c2..ab503dba8427c 100644 --- a/app/assets/stylesheets/administrate/components/_main-content.scss +++ b/app/assets/stylesheets/administrate/components/_main-content.scss @@ -13,6 +13,10 @@ table { font-size: $font-size-small; } + + form { + margin-top: $space-two; + } } .main-content__header { @@ -20,7 +24,7 @@ background-color: $color-white; border-bottom: 1px solid $color-border; display: flex; - min-height: 5.6rem; + min-height: 3.5rem; padding: $space-small $space-normal; } diff --git a/app/assets/stylesheets/administrate/components/_navigation.scss b/app/assets/stylesheets/administrate/components/_navigation.scss deleted file mode 100644 index 4ffc421903a9f..0000000000000 --- a/app/assets/stylesheets/administrate/components/_navigation.scss +++ /dev/null @@ -1,88 +0,0 @@ -.logo-brand { - margin-bottom: $space-normal; - padding: $space-normal $space-smaller $space-small; - text-align: left; - - img { - margin-bottom: $space-smaller; - max-height: 3rem; - } -} - -.navigation { - background: $white; - border-right: 1px solid $color-border; - display: flex; - flex-direction: column; - font-size: $font-size-default; - font-weight: $font-weight-medium; - height: 100%; - justify-content: flex-start; - left: 0; - margin: 0; - overflow: auto; - padding: $space-normal; - position: fixed; - top: 0; - width: 21rem; - z-index: 1023; - - li { - align-items: center; - display: flex; - font-size: $font-size-small; - - a { - color: $color-gray; - text-decoration: none; - } - - i { - min-width: $space-medium; - } - } - - hr { - margin: $space-slab; - } -} - -.navigation__link { - background-color: transparent; - color: $color-gray; - display: block; - line-height: 1; - margin-bottom: $space-smaller; - padding: $space-small; - - &:hover { - color: $blue; - - a { - color: $blue; - } - } - - - &.navigation__link--active { - background-color: $color-background; - border-radius: $base-border-radius; - color: $blue; - - a { - color: $blue; - } - } -} - -.logout { - bottom: $space-normal; - left: $space-normal; - position: fixed; -} - -.app-version { - color: $color-gray; - font-size: $font-size-small; - padding-top: $space-smaller; -} diff --git a/app/assets/stylesheets/administrate/components/_search.scss b/app/assets/stylesheets/administrate/components/_search.scss index f3a25961829ab..bd5c2eece2284 100644 --- a/app/assets/stylesheets/administrate/components/_search.scss +++ b/app/assets/stylesheets/administrate/components/_search.scss @@ -1,7 +1,7 @@ .search { margin-left: auto; - margin-right: 2rem; - max-width: 44rem; + margin-right: 1.25rem; + max-width: 27.5rem; position: relative; width: 100%; } diff --git a/app/assets/stylesheets/administrate/library/_variables.scss b/app/assets/stylesheets/administrate/library/_variables.scss index 08642855603a2..2e424fb06649a 100644 --- a/app/assets/stylesheets/administrate/library/_variables.scss +++ b/app/assets/stylesheets/administrate/library/_variables.scss @@ -1,10 +1,10 @@ // Typography -$base-font-family: PlusJakarta, Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", +$base-font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif !default; $heading-font-family: $base-font-family !default; -$base-font-size: 14px !default; +$base-font-size: 16px !default; $base-line-height: 1.5 !default; $heading-line-height: 1.2 !default; diff --git a/app/assets/stylesheets/administrate/utilities/_variables.scss b/app/assets/stylesheets/administrate/utilities/_variables.scss index 818f96e4c89dd..3a1129c41497f 100644 --- a/app/assets/stylesheets/administrate/utilities/_variables.scss +++ b/app/assets/stylesheets/administrate/utilities/_variables.scss @@ -1,30 +1,30 @@ // Font sizes -$font-size-nano: 0.8rem; -$font-size-micro: 1.0rem; -$font-size-mini: 1.2rem; -$font-size-small: 1.4rem; -$font-size-default: 1.6rem; -$font-size-medium: 1.8rem; -$font-size-large: 2.2rem; -$font-size-big: 2.4rem; -$font-size-bigger: 3.0rem; -$font-size-mega: 3.4rem; -$font-size-giga: 4.0rem; +$font-size-nano: 0.5rem; +$font-size-micro: 0.675rem; +$font-size-mini: 0.75rem; +$font-size-small: 0.875rem; +$font-size-default: 1rem; +$font-size-medium: 1.125rem; +$font-size-large: 1.375rem; +$font-size-big: 1.5rem; +$font-size-bigger: 1.75rem; +$font-size-mega: 2.125rem; +$font-size-giga: 2.5rem; // spaces $zero: 0; -$space-micro: 0.2rem; -$space-smaller: 0.4rem; -$space-small: 0.8rem; -$space-one: 1rem; -$space-slab: 1.2rem; -$space-normal: 1.6rem; -$space-two: 2.0rem; -$space-medium: 2.4rem; -$space-large: 3.2rem; -$space-larger: 4.8rem; -$space-jumbo: 6.4rem; -$space-mega: 10.0rem; +$space-micro: 0.125rem; +$space-smaller: 0.25rem; +$space-small: 0.5rem; +$space-one: 0.675rem; +$space-slab: 0.75rem; +$space-normal: 1rem; +$space-two: 1.25rem; +$space-medium: 1.5rem; +$space-large: 2rem; +$space-larger: 3rem; +$space-jumbo: 4rem; +$space-mega: 6.25rem; // font-weight $font-weight-feather: 100; diff --git a/app/builders/messages/facebook/message_builder.rb b/app/builders/messages/facebook/message_builder.rb index 3efb184b96225..fec298bced8cb 100644 --- a/app/builders/messages/facebook/message_builder.rb +++ b/app/builders/messages/facebook/message_builder.rb @@ -25,7 +25,9 @@ def perform build_contact_inbox build_message end - rescue Koala::Facebook::AuthenticationError + rescue Koala::Facebook::AuthenticationError => e + Rails.logger.warn("Facebook authentication error for inbox: #{@inbox.id} with error: #{e.message}") + Rails.logger.error e @inbox.channel.authorization_error! rescue StandardError => e ChatwootExceptionTracker.new(e, account: @inbox.account).capture_exception @@ -108,11 +110,15 @@ def process_contact_params_result(result) } end + # rubocop:disable Metrics/AbcSize + # rubocop:disable Metrics/MethodLength def contact_params begin k = Koala::Facebook::API.new(@inbox.channel.page_access_token) if @inbox.facebook? result = k.get_object(@sender_id) || {} - rescue Koala::Facebook::AuthenticationError + rescue Koala::Facebook::AuthenticationError => e + Rails.logger.warn("Facebook authentication error for inbox: #{@inbox.id} with error: #{e.message}") + Rails.logger.error e @inbox.channel.authorization_error! raise rescue Koala::Facebook::ClientError => e @@ -130,4 +136,6 @@ def contact_params end process_contact_params_result(result) end + # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/MethodLength end diff --git a/app/builders/messages/instagram/message_builder.rb b/app/builders/messages/instagram/message_builder.rb index 5b2243906f032..5610e0671ce2c 100644 --- a/app/builders/messages/instagram/message_builder.rb +++ b/app/builders/messages/instagram/message_builder.rb @@ -20,7 +20,9 @@ def perform ActiveRecord::Base.transaction do build_message end - rescue Koala::Facebook::AuthenticationError + rescue Koala::Facebook::AuthenticationError => e + Rails.logger.warn("Instagram authentication error for inbox: #{@inbox.id} with error: #{e.message}") + Rails.logger.error e @inbox.channel.authorization_error! raise rescue StandardError => e diff --git a/app/builders/messages/message_builder.rb b/app/builders/messages/message_builder.rb index 0ec73edc33e51..e1087b19f8936 100644 --- a/app/builders/messages/message_builder.rb +++ b/app/builders/messages/message_builder.rb @@ -149,7 +149,8 @@ def message_params content_type: @params[:content_type], items: @items, in_reply_to: @in_reply_to, - echo_id: @params[:echo_id] + echo_id: @params[:echo_id], + source_id: @params[:source_id] }.merge(external_created_at).merge(automation_rule_id).merge(campaign_id).merge(template_params) end end diff --git a/app/builders/notification_builder.rb b/app/builders/notification_builder.rb index 6d2096233aa2c..8efe44fd6da41 100644 --- a/app/builders/notification_builder.rb +++ b/app/builders/notification_builder.rb @@ -1,5 +1,5 @@ class NotificationBuilder - pattr_initialize [:notification_type!, :user!, :account!, :primary_actor!] + pattr_initialize [:notification_type!, :user!, :account!, :primary_actor!, :secondary_actor] def perform return unless user_subscribed_to_notification? @@ -9,7 +9,7 @@ def perform private - def secondary_actor + def current_user Current.user end @@ -29,7 +29,8 @@ def build_notification notification_type: notification_type, account: account, primary_actor: primary_actor, - secondary_actor: secondary_actor + # secondary_actor is secondary_actor if present, else current_user + secondary_actor: secondary_actor || current_user ) end end diff --git a/app/controllers/api/v1/accounts/agent_bots_controller.rb b/app/controllers/api/v1/accounts/agent_bots_controller.rb index efb48c5c6e3b4..43bce17bce6ce 100644 --- a/app/controllers/api/v1/accounts/agent_bots_controller.rb +++ b/app/controllers/api/v1/accounts/agent_bots_controller.rb @@ -10,11 +10,18 @@ def index def show; end def create - @agent_bot = Current.account.agent_bots.create!(permitted_params) + @agent_bot = Current.account.agent_bots.create!(permitted_params.except(:avatar_url)) + process_avatar_from_url end def update - @agent_bot.update!(permitted_params) + @agent_bot.update!(permitted_params.except(:avatar_url)) + process_avatar_from_url + end + + def avatar + @agent_bot.avatar.purge if @agent_bot.avatar.attached? + @agent_bot end def destroy @@ -30,6 +37,10 @@ def agent_bot end def permitted_params - params.permit(:name, :description, :outgoing_url, :bot_type, bot_config: [:csml_content]) + params.permit(:name, :description, :outgoing_url, :avatar, :avatar_url, :bot_type, bot_config: [:csml_content]) + end + + def process_avatar_from_url + ::Avatar::AvatarFromUrlJob.perform_later(@agent_bot, params[:avatar_url]) if params[:avatar_url].present? end end diff --git a/app/controllers/api/v1/accounts/contacts_controller.rb b/app/controllers/api/v1/accounts/contacts_controller.rb index 35784ebd53c87..7171c9276b4af 100644 --- a/app/controllers/api/v1/accounts/contacts_controller.rb +++ b/app/controllers/api/v1/accounts/contacts_controller.rb @@ -90,14 +90,14 @@ def create @contact = Current.account.contacts.new(permitted_params.except(:avatar_url)) @contact.save! @contact_inbox = build_contact_inbox - process_avatar + process_avatar_from_url end end def update @contact.assign_attributes(contact_update_params) @contact.save! - process_avatar if permitted_params[:avatar].present? || permitted_params[:avatar_url].present? + process_avatar_from_url end def destroy @@ -181,7 +181,7 @@ def fetch_contact @contact = Current.account.contacts.includes(contact_inboxes: [:inbox]).find(params[:id]) end - def process_avatar + def process_avatar_from_url ::Avatar::AvatarFromUrlJob.perform_later(@contact, params[:avatar_url]) if params[:avatar_url].present? end diff --git a/app/controllers/api/v1/accounts/conversations/assignments_controller.rb b/app/controllers/api/v1/accounts/conversations/assignments_controller.rb index 68ff2e67d8445..1fb2095e3f2d2 100644 --- a/app/controllers/api/v1/accounts/conversations/assignments_controller.rb +++ b/app/controllers/api/v1/accounts/conversations/assignments_controller.rb @@ -14,7 +14,8 @@ def create def set_agent @agent = Current.account.users.find_by(id: params[:assignee_id]) - @conversation.update_assignee(@agent) + @conversation.assignee = @agent + @conversation.save! render_agent end diff --git a/app/controllers/api/v1/accounts/conversations/messages_controller.rb b/app/controllers/api/v1/accounts/conversations/messages_controller.rb index 319c6763f5f6a..d34561e36589f 100644 --- a/app/controllers/api/v1/accounts/conversations/messages_controller.rb +++ b/app/controllers/api/v1/accounts/conversations/messages_controller.rb @@ -18,6 +18,15 @@ def destroy end end + def retry + return if message.blank? + + message.update!(status: :sent, content_attributes: {}) + ::SendReplyJob.perform_later(message.id) + rescue StandardError => e + render_could_not_create_error(e.message) + end + def translate return head :ok if already_translated_content_available? diff --git a/app/controllers/api/v1/accounts/conversations_controller.rb b/app/controllers/api/v1/accounts/conversations_controller.rb index d4993991270b7..ae1828c9e500a 100644 --- a/app/controllers/api/v1/accounts/conversations_controller.rb +++ b/app/controllers/api/v1/accounts/conversations_controller.rb @@ -114,8 +114,8 @@ def set_conversation_status end def assign_conversation - @agent = Current.account.users.find(current_user.id) - @conversation.update_assignee(@agent) + @conversation.assignee = current_user + @conversation.save! end def conversation diff --git a/app/controllers/api/v1/accounts/notifications_controller.rb b/app/controllers/api/v1/accounts/notifications_controller.rb index fb23370c5c68c..0d8cf6a47a2aa 100644 --- a/app/controllers/api/v1/accounts/notifications_controller.rb +++ b/app/controllers/api/v1/accounts/notifications_controller.rb @@ -1,7 +1,8 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseController RESULTS_PER_PAGE = 15 + include DateRangeHelper - before_action :fetch_notification, only: [:update, :destroy] + before_action :fetch_notification, only: [:update, :destroy, :snooze] before_action :set_primary_actor, only: [:read_all] before_action :set_current_page, only: [:index] @@ -38,6 +39,11 @@ def unread_count render json: @unread_count end + def snooze + @notification.update(snoozed_until: parse_date_time(params[:snoozed_until].to_s)) if params[:snoozed_until] + render json: @notification + end + private def set_primary_actor diff --git a/app/controllers/platform/api/v1/agent_bots_controller.rb b/app/controllers/platform/api/v1/agent_bots_controller.rb index 138052b77b497..dd70a1ba5e96a 100644 --- a/app/controllers/platform/api/v1/agent_bots_controller.rb +++ b/app/controllers/platform/api/v1/agent_bots_controller.rb @@ -9,13 +9,15 @@ def index def show; end def create - @resource = AgentBot.new(agent_bot_params) + @resource = AgentBot.new(agent_bot_params.except(:avatar_url)) @resource.save! + process_avatar_from_url @platform_app.platform_app_permissibles.find_or_create_by(permissible: @resource) end def update - @resource.update!(agent_bot_params) + @resource.update!(agent_bot_params.except(:avatar_url)) + process_avatar_from_url end def destroy @@ -23,6 +25,11 @@ def destroy head :ok end + def avatar + @resource.avatar.purge if @resource.avatar.attached? + @resource + end + private def set_resource @@ -30,6 +37,10 @@ def set_resource end def agent_bot_params - params.permit(:name, :description, :account_id, :outgoing_url) + params.permit(:name, :description, :account_id, :outgoing_url, :avatar, :avatar_url) + end + + def process_avatar_from_url + ::Avatar::AvatarFromUrlJob.perform_later(@resource, params[:avatar_url]) if params[:avatar_url].present? end end diff --git a/app/controllers/super_admin/app_configs_controller.rb b/app/controllers/super_admin/app_configs_controller.rb index 2899a7defce4f..ce06879c571dd 100644 --- a/app/controllers/super_admin/app_configs_controller.rb +++ b/app/controllers/super_admin/app_configs_controller.rb @@ -1,21 +1,38 @@ class SuperAdmin::AppConfigsController < SuperAdmin::ApplicationController + before_action :set_config + before_action :allowed_configs def show - @allowed_configs = %w[FB_APP_ID FB_VERIFY_TOKEN FB_APP_SECRET] # ref: https://github.com/rubocop/rubocop/issues/7767 # rubocop:disable Style/HashTransformValues - @fb_config = InstallationConfig.where(name: @allowed_configs) - .pluck(:name, :serialized_value) - .map { |name, serialized_value| [name, serialized_value['value']] } - .to_h + @app_config = InstallationConfig.where(name: @allowed_configs) + .pluck(:name, :serialized_value) + .map { |name, serialized_value| [name, serialized_value['value']] } + .to_h # rubocop:enable Style/HashTransformValues end def create params['app_config'].each do |key, value| + next unless @allowed_configs.include?(key) + i = InstallationConfig.where(name: key).first_or_create(value: value, locked: false) i.value = value i.save! end - redirect_to super_admin_app_config_url + # rubocop:disable Rails/I18nLocaleTexts + redirect_to super_admin_settings_path, notice: 'App Configs updated successfully' + # rubocop:enable Rails/I18nLocaleTexts + end + + private + + def set_config + @config = params[:config] + end + + def allowed_configs + @allowed_configs = %w[FB_APP_ID FB_VERIFY_TOKEN FB_APP_SECRET] end end + +SuperAdmin::AppConfigsController.prepend_mod_with('SuperAdmin::AppConfigsController') diff --git a/app/controllers/super_admin/application_controller.rb b/app/controllers/super_admin/application_controller.rb index 69b61b913d510..3b98a6e21832b 100644 --- a/app/controllers/super_admin/application_controller.rb +++ b/app/controllers/super_admin/application_controller.rb @@ -20,4 +20,13 @@ def order params.fetch(resource_name, {}).fetch(:direction, 'desc') ) end + + private + + def invalid_action_perfomed + # rubocop:disable Rails/I18nLocaleTexts + flash[:error] = 'Invalid action performed' + # rubocop:enable Rails/I18nLocaleTexts + redirect_back(fallback_location: root_path) + end end diff --git a/app/controllers/super_admin/installation_configs_controller.rb b/app/controllers/super_admin/installation_configs_controller.rb index 36a45f7073219..b1f15b518c11b 100644 --- a/app/controllers/super_admin/installation_configs_controller.rb +++ b/app/controllers/super_admin/installation_configs_controller.rb @@ -1,4 +1,5 @@ class SuperAdmin::InstallationConfigsController < SuperAdmin::ApplicationController + rescue_from ActiveRecord::RecordNotUnique, :with => :invalid_action_perfomed # Overwrite any of the RESTful controller actions to implement custom behavior # For example, you may want to send an email after a foo is updated. # diff --git a/app/controllers/super_admin/settings_controller.rb b/app/controllers/super_admin/settings_controller.rb new file mode 100644 index 0000000000000..685e6a8bdd057 --- /dev/null +++ b/app/controllers/super_admin/settings_controller.rb @@ -0,0 +1,10 @@ +class SuperAdmin::SettingsController < SuperAdmin::ApplicationController + def show; end + + def refresh + Internal::CheckNewVersionsJob.perform_now + # rubocop:disable Rails/I18nLocaleTexts + redirect_to super_admin_settings_path, notice: 'Instance status refreshed' + # rubocop:enable Rails/I18nLocaleTexts + end +end diff --git a/app/dashboards/access_token_dashboard.rb b/app/dashboards/access_token_dashboard.rb index d3f05a7994e4a..927aecadde500 100644 --- a/app/dashboards/access_token_dashboard.rb +++ b/app/dashboards/access_token_dashboard.rb @@ -30,11 +30,7 @@ class AccessTokenDashboard < Administrate::BaseDashboard # SHOW_PAGE_ATTRIBUTES # an array of attributes that will be displayed on the model's show page. SHOW_PAGE_ATTRIBUTES = %i[ - owner - id token - created_at - updated_at ].freeze # FORM_ATTRIBUTES diff --git a/app/dashboards/agent_bot_dashboard.rb b/app/dashboards/agent_bot_dashboard.rb index baeb6e814ed17..a253b2406811d 100644 --- a/app/dashboards/agent_bot_dashboard.rb +++ b/app/dashboards/agent_bot_dashboard.rb @@ -46,6 +46,7 @@ class AgentBotDashboard < Administrate::BaseDashboard name description outgoing_url + access_token ].freeze # FORM_ATTRIBUTES diff --git a/app/dashboards/platform_app_dashboard.rb b/app/dashboards/platform_app_dashboard.rb index f5ed564effad1..80fc7c5cda270 100644 --- a/app/dashboards/platform_app_dashboard.rb +++ b/app/dashboards/platform_app_dashboard.rb @@ -32,6 +32,7 @@ class PlatformAppDashboard < Administrate::BaseDashboard name created_at updated_at + access_token ].freeze # FORM_ATTRIBUTES diff --git a/app/dashboards/user_dashboard.rb b/app/dashboards/user_dashboard.rb index e00d06a72d203..6b2129eedcadc 100644 --- a/app/dashboards/user_dashboard.rb +++ b/app/dashboards/user_dashboard.rb @@ -36,7 +36,8 @@ class UserDashboard < Administrate::BaseDashboard updated_at: Field::DateTime, pubsub_token: Field::String, type: Field::Select.with_options(collection: [nil, 'SuperAdmin']), - accounts: CountField + accounts: CountField, + access_token: Field::HasOne }.freeze # COLLECTION_ATTRIBUTES @@ -67,6 +68,7 @@ class UserDashboard < Administrate::BaseDashboard updated_at confirmed_at account_users + access_token ].freeze # FORM_ATTRIBUTES diff --git a/app/drops/contact_drop.rb b/app/drops/contact_drop.rb index 6611ecdc399ce..1d450adb76086 100644 --- a/app/drops/contact_drop.rb +++ b/app/drops/contact_drop.rb @@ -18,4 +18,9 @@ def first_name def last_name @obj.try(:name).try(:split).try(:last).try(:capitalize) if @obj.try(:name).try(:split).try(:size) > 1 end + + def custom_attribute + custom_attributes = @obj.try(:custom_attributes) || {} + custom_attributes.transform_keys(&:to_s) + end end diff --git a/app/drops/conversation_drop.rb b/app/drops/conversation_drop.rb index d77e150c222d5..d62642885bee5 100644 --- a/app/drops/conversation_drop.rb +++ b/app/drops/conversation_drop.rb @@ -19,6 +19,11 @@ def recent_messages end end + def custom_attribute + custom_attributes = @obj.try(:custom_attributes) || {} + custom_attributes.transform_keys(&:to_s) + end + private def message_sender_name(sender) diff --git a/app/finders/conversation_finder.rb b/app/finders/conversation_finder.rb index cbbd60caaccaf..0cc5e52d81544 100644 --- a/app/finders/conversation_finder.rb +++ b/app/finders/conversation_finder.rb @@ -3,13 +3,21 @@ class ConversationFinder DEFAULT_STATUS = 'open'.freeze SORT_OPTIONS = { - latest: 'latest', - sort_on_created_at: 'sort_on_created_at', - last_user_message_at: 'last_user_message_at', - sort_on_priority: 'sort_on_priority', - sort_on_waiting_since: 'sort_on_waiting_since' + 'last_activity_at_asc' => %w[sort_on_last_activity_at asc], + 'last_activity_at_desc' => %w[sort_on_last_activity_at desc], + 'created_at_asc' => %w[sort_on_created_at asc], + 'created_at_desc' => %w[sort_on_created_at desc], + 'priority_asc' => %w[sort_on_priority asc], + 'priority_desc' => %w[sort_on_priority desc], + 'waiting_since_asc' => %w[sort_on_waiting_since asc], + 'waiting_since_desc' => %w[sort_on_waiting_since desc], + + # To be removed in v3.5.0 + 'latest' => %w[sort_on_last_activity_at desc], + 'sort_on_created_at' => %w[sort_on_created_at asc], + 'sort_on_priority' => %w[sort_on_priority desc], + 'sort_on_waiting_since' => %w[sort_on_waiting_since asc] }.with_indifferent_access - # assumptions # inbox_id if not given, take from all conversations, else specific to inbox # assignee_type if not given, take 'all' @@ -159,7 +167,8 @@ def conversations @conversations = @conversations.includes( :taggings, :inbox, { assignee: { avatar_attachment: [:blob] } }, { contact: { avatar_attachment: [:blob] } }, :team, :contact_inbox ) - sort_by = SORT_OPTIONS[params[:sort_by]] || SORT_OPTIONS['latest'] - @conversations.send(sort_by).page(current_page) + + 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) end end diff --git a/app/helpers/portal_helper.rb b/app/helpers/portal_helper.rb index ef16e9797019a..342bb62aaf3f3 100644 --- a/app/helpers/portal_helper.rb +++ b/app/helpers/portal_helper.rb @@ -64,6 +64,8 @@ def render_category_content(content) def thumbnail_bg_color(username) colors = ['#6D95BA', '#A4C3C3', '#E19191'] + return colors.sample if username.blank? + colors[username.length % colors.size] end end diff --git a/app/javascript/dashboard/api/inbox/message.js b/app/javascript/dashboard/api/inbox/message.js index 1798928ada14f..fad7822c5bda6 100644 --- a/app/javascript/dashboard/api/inbox/message.js +++ b/app/javascript/dashboard/api/inbox/message.js @@ -97,6 +97,12 @@ class MessageApi extends ApiClient { return axios.delete(`${this.url}/${conversationID}/messages/${messageId}`); } + retry(conversationID, messageId) { + return axios.post( + `${this.url}/${conversationID}/messages/${messageId}/retry` + ); + } + getPreviousMessages({ conversationId, after, before }) { const params = { before }; if (after && Number(after) !== Number(before)) { diff --git a/app/javascript/dashboard/assets/images/typing.gif b/app/javascript/dashboard/assets/images/typing.gif index dd9b1ca2b15bd..b288d2559786c 100644 Binary files a/app/javascript/dashboard/assets/images/typing.gif and b/app/javascript/dashboard/assets/images/typing.gif differ diff --git a/app/javascript/dashboard/assets/scss/super_admin/index.scss b/app/javascript/dashboard/assets/scss/super_admin/index.scss index f95f1303f475b..91f7835d03a7b 100644 --- a/app/javascript/dashboard/assets/scss/super_admin/index.scss +++ b/app/javascript/dashboard/assets/scss/super_admin/index.scss @@ -1,39 +1,8 @@ -@import '../variables'; +@import 'shared/assets/fonts/inter'; +@import 'tailwindcss/base'; +@import 'tailwindcss/components'; +@import 'tailwindcss/utilities'; -.superadmin-body { - background: var(--color-background); - - .hero--title { - font-size: var(--font-size-mega); - font-weight: var(--font-weight-light); - margin-top: var(--space-large); - } - - .update-subscription--checkbox { - display: flex; - - input { - line-height: 1.5; - margin-right: var(--space-one); - margin-top: var(--space-smaller); - } - - label { - font-size: var(--font-size-small); - line-height: 1.5; - margin-bottom: var(--space-normal); - } - } +body { + font-family: Inter, -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important; } - -.alert-box { - background-color: var(--r-500); - border-radius: 5px; - color: var(--color-white); - font-size: 14px; - margin-bottom: 14px; - padding: 10px; - text-align: center; -} - - diff --git a/app/javascript/dashboard/assets/scss/super_admin/pages.scss b/app/javascript/dashboard/assets/scss/super_admin/pages.scss deleted file mode 100644 index a33da2693d4fa..0000000000000 --- a/app/javascript/dashboard/assets/scss/super_admin/pages.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import 'shared/assets/fonts/plus-jakarta'; -@import '../variables'; -@import '~shared/assets/stylesheets/ionicons'; diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index 194bf5e5d32a0..f3e901d37d9d0 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -126,49 +126,33 @@ @assign-team="onAssignTeamsForBulk" />
- {{ $t('CHAT_LIST.EOF') }} -
+ ++ {{ $t('CHAT_LIST.EOF') }} +
+- {{ - useInstallationName( - $t('NETWORK.NOTIFICATION.TEXT'), - globalConfig.installationName - ) - }} -
-