diff --git a/Gemfile b/Gemfile index 680a0738b..7533cf3cf 100644 --- a/Gemfile +++ b/Gemfile @@ -76,7 +76,7 @@ gem 'faraday_middleware-aws-sigv4' ##--- gems for server & infra configuration ---## gem 'dotenv-rails', '>= 3.0.0' gem 'foreman' -gem 'puma' +gem 'puma', '~> 7.2', '>= 7.2.1' gem 'vite_rails' # metrics on heroku gem 'barnes' diff --git a/Gemfile.lock b/Gemfile.lock index 7151d0ff1..141afc122 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -136,6 +136,8 @@ GEM audited (5.4.1) activerecord (>= 5.0, < 7.7) activesupport (>= 5.0, < 7.7) + auth-sanitizer (0.2.1) + version_gem (~> 1.1, >= 1.1.10) aws-actionmailbox-ses (0.1.0) actionmailbox (>= 7.1.0) aws-sdk-s3 (~> 1, >= 1.123.0) @@ -168,7 +170,7 @@ GEM base64 (0.3.0) bcrypt (3.1.22) benchmark (0.4.1) - bigdecimal (3.2.2) + bigdecimal (3.3.1) bindex (0.8.1) bootsnap (1.16.0) msgpack (~> 1.2) @@ -184,6 +186,7 @@ GEM bundler (>= 1.2.0, < 3) thor (~> 1.0) byebug (11.1.3) + cgi (0.5.1) childprocess (5.1.0) logger (~> 1.5) cld3 (3.7.0) @@ -312,7 +315,7 @@ GEM hashie faraday-multipart (1.2.0) multipart-post (~> 2.0) - faraday-net_http (3.4.2) + faraday-net_http (3.4.4) net-http (~> 0.5) faraday-net_http_persistent (2.1.0) faraday (~> 2.5) @@ -435,7 +438,8 @@ GEM hana (1.3.7) hash_diff (1.1.1) hashdiff (1.1.0) - hashie (5.0.0) + hashie (5.1.0) + logger html2text (0.4.0) nokogiri (>= 1.0, < 2.0) http (5.1.1) @@ -470,7 +474,7 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.19.5) + json (2.19.8) json_refs (0.1.8) hana json_schemer (0.2.24) @@ -568,7 +572,7 @@ GEM ruby2_keywords msgpack (1.8.0) multi_json (1.15.0) - multi_xml (0.8.0) + multi_xml (0.9.1) bigdecimal (>= 3.1, < 5) multipart-post (2.4.1) mutex_m (0.3.0) @@ -593,7 +597,7 @@ GEM sidekiq newrelic_rpm (9.6.0) base64 - nio4r (2.7.3) + nio4r (2.7.5) nokogiri (1.19.3) mini_portile2 (~> 2.8.2) racc (~> 1.4) @@ -603,19 +607,26 @@ GEM racc (~> 1.4) nokogiri (1.19.3-x86_64-linux-gnu) racc (~> 1.4) - oauth (1.1.0) - oauth-tty (~> 1.0, >= 1.0.1) - snaky_hash (~> 2.0) - version_gem (~> 1.1) - oauth-tty (1.0.5) - version_gem (~> 1.1, >= 1.1.1) - oauth2 (2.0.9) - faraday (>= 0.17.3, < 3.0) - jwt (>= 1.0, < 3.0) + oauth (1.1.6) + auth-sanitizer (~> 0.2, >= 0.2.1) + base64 (~> 0.1) + cgi + oauth-tty (~> 1.0, >= 1.0.8) + snaky_hash (~> 2.0, >= 2.0.5) + version_gem (~> 1.1, >= 1.1.11) + oauth-tty (1.0.8) + auth-sanitizer (~> 0.1, >= 0.1.3) + cgi + version_gem (~> 1.1, >= 1.1.9) + oauth2 (2.0.22) + auth-sanitizer (~> 0.2, >= 0.2.1) + faraday (>= 0.17.3, < 4.0) + jwt (>= 1.0, < 4.0) + logger (~> 1.2) multi_xml (~> 0.5) rack (>= 1.2, < 4) - snaky_hash (~> 2.0) - version_gem (~> 1.1) + snaky_hash (~> 2.0, >= 2.0.5) + version_gem (~> 1.1, >= 1.1.11) oj (3.16.10) bigdecimal (>= 3.0) ostruct (>= 0.2) @@ -682,7 +693,7 @@ GEM pry-rails (0.3.9) pry (>= 0.10.4) public_suffix (7.0.5) - puma (6.4.3) + puma (7.2.1) nio4r (~> 2.0) pundit (2.3.0) activesupport (>= 3.0.0) @@ -935,9 +946,9 @@ GEM gli hashie logger - snaky_hash (2.0.1) - hashie - version_gem (~> 1.1, >= 1.1.1) + snaky_hash (2.0.5) + hashie (>= 0.1.0, < 6) + version_gem (>= 1.1.8, < 3) sorbet-runtime (0.5.11934) spring (4.1.1) spring-watcher-listen (2.1.0) @@ -995,7 +1006,7 @@ GEM valid_email2 (5.2.6) activemodel (>= 3.2) mail (~> 2.5) - version_gem (1.1.4) + version_gem (1.1.11) vite_rails (3.10.0) railties (>= 5.1, < 9) vite_ruby (~> 3.0, >= 3.2.2) @@ -1132,7 +1143,7 @@ DEPENDENCIES pgvector procore-sift pry-rails - puma + puma (~> 7.2, >= 7.2.1) pundit rack-attack (>= 6.7.0) rack-cors (= 2.0.0) diff --git a/VERSION_CW b/VERSION_CW index d2b9909a9..0fb7a35b6 100644 --- a/VERSION_CW +++ b/VERSION_CW @@ -1 +1 @@ -4.14.1 +4.14.2 diff --git a/app/builders/contact_inbox_with_contact_builder.rb b/app/builders/contact_inbox_with_contact_builder.rb index 2c0e6087e..2ba9179ed 100644 --- a/app/builders/contact_inbox_with_contact_builder.rb +++ b/app/builders/contact_inbox_with_contact_builder.rb @@ -50,7 +50,7 @@ class ContactInboxWithContactBuilder def create_contact account.contacts.create!( - name: contact_attributes[:name] || ::Haikunator.haikunate(1000), + name: contact_name, phone_number: contact_attributes[:phone_number], email: contact_attributes[:email], identifier: contact_attributes[:identifier], @@ -59,6 +59,11 @@ class ContactInboxWithContactBuilder ) end + def contact_name + name = contact_attributes[:name] || ::Haikunator.haikunate(1000) + name.truncate(ApplicationRecord::MAX_STRING_COLUMN_LENGTH, omission: '') + end + def find_contact contact = find_contact_by_identifier(contact_attributes[:identifier]) contact ||= find_contact_by_email(contact_attributes[:email]) diff --git a/app/controllers/api/v1/accounts/onboardings_controller.rb b/app/controllers/api/v1/accounts/onboardings_controller.rb index 77c6245ea..181e4965e 100644 --- a/app/controllers/api/v1/accounts/onboardings_controller.rb +++ b/app/controllers/api/v1/accounts/onboardings_controller.rb @@ -16,6 +16,10 @@ class Api::V1::Accounts::OnboardingsController < Api::V1::Accounts::BaseControll render 'api/v1/accounts/update', format: :json end + def help_center_generation + render json: help_center_generation_status + end + private def finalizing_account_details? @@ -33,4 +37,15 @@ class Api::V1::Accounts::OnboardingsController < Api::V1::Accounts::BaseControll def custom_attributes_params params.permit(:industry, :company_size, :timezone, :referral_source, :user_role, :website) end + + def help_center_generation_status + { + generation_id: nil, + state: nil, + articles_count: 0, + categories_count: 0 + } + end end + +Api::V1::Accounts::OnboardingsController.prepend_mod_with('Api::V1::Accounts::OnboardingsController') diff --git a/app/controllers/api/v1/accounts/portals_controller.rb b/app/controllers/api/v1/accounts/portals_controller.rb index 770018e3c..c74c0ecfc 100644 --- a/app/controllers/api/v1/accounts/portals_controller.rb +++ b/app/controllers/api/v1/accounts/portals_controller.rb @@ -80,10 +80,15 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController :id, :color, :custom_domain, :header_text, :homepage_link, :name, :page_title, :slug, :archived, { config: [:default_locale, :layout, { allowed_locales: [] }, { draft_locales: [] }, - { social_profiles: %i[facebook x instagram linkedin youtube tiktok github whatsapp] }] } + { social_profiles: %i[facebook x instagram linkedin youtube tiktok github whatsapp] }, + { locale_translations: locale_translation_keys.index_with { %i[name page_title header_text] } }] } ) end + def locale_translation_keys + params.dig(:portal, :config, :locale_translations)&.keys || [] + end + def live_chat_widget_params permitted_params = params.permit(:inbox_id) return {} unless permitted_params.key?(:inbox_id) diff --git a/app/controllers/concerns/access_token_auth_helper.rb b/app/controllers/concerns/access_token_auth_helper.rb index b7fc14e74..fb52a8eeb 100644 --- a/app/controllers/concerns/access_token_auth_helper.rb +++ b/app/controllers/concerns/access_token_auth_helper.rb @@ -1,8 +1,9 @@ module AccessTokenAuthHelper BOT_ACCESSIBLE_ENDPOINTS = { - 'api/v1/accounts/conversations' => %w[toggle_status toggle_typing_status toggle_priority create update custom_attributes], + 'api/v1/accounts/conversations' => %w[show toggle_status toggle_typing_status toggle_priority create update custom_attributes], 'api/v1/accounts/conversations/messages' => ['create'], - 'api/v1/accounts/conversations/assignments' => ['create'] + 'api/v1/accounts/conversations/assignments' => ['create'], + 'api/v1/accounts/conversations/labels' => %w[index create] }.freeze def ensure_access_token diff --git a/app/controllers/public/api/v1/portals_controller.rb b/app/controllers/public/api/v1/portals_controller.rb index 63f44b052..57db11aec 100644 --- a/app/controllers/public/api/v1/portals_controller.rb +++ b/app/controllers/public/api/v1/portals_controller.rb @@ -9,7 +9,7 @@ class Public::Api::V1::PortalsController < Public::Api::V1::Portals::BaseControl layout 'portal' def show - @og_image_url = helpers.set_og_image_url('', @portal.header_text) + @og_image_url = helpers.set_og_image_url('', @portal.localized_value('header_text', @locale)) end def sitemap diff --git a/app/drops/contact_drop.rb b/app/drops/contact_drop.rb index 1d450adb7..16240ec06 100644 --- a/app/drops/contact_drop.rb +++ b/app/drops/contact_drop.rb @@ -12,7 +12,7 @@ class ContactDrop < BaseDrop end def first_name - @obj.try(:name).try(:split).try(:first).try(:capitalize) if @obj.try(:name).try(:split).try(:size) > 1 + @obj.try(:name).try(:split).try(:first).try(:capitalize) end def last_name diff --git a/app/drops/user_drop.rb b/app/drops/user_drop.rb index cf6f1b6a1..83d5e2347 100644 --- a/app/drops/user_drop.rb +++ b/app/drops/user_drop.rb @@ -12,7 +12,7 @@ class UserDrop < BaseDrop end def first_name - @obj.try(:name).try(:split).try(:first).try(:capitalize) if @obj.try(:name).try(:split).try(:size).to_i > 1 + @obj.try(:name).try(:split).try(:first).try(:capitalize) end def last_name diff --git a/app/finders/conversation_finder.rb b/app/finders/conversation_finder.rb index fa437327d..1c27d8260 100644 --- a/app/finders/conversation_finder.rb +++ b/app/finders/conversation_finder.rb @@ -40,7 +40,7 @@ class ConversationFinder def perform set_up - mine_count, unassigned_count, all_count, = set_count_for_all_conversations + mine_count, unassigned_count, all_count = set_count_for_all_conversations assigned_count = all_count - unassigned_count filter_by_assignee_type @@ -184,6 +184,17 @@ class ConversationFinder end def set_count_for_all_conversations + return legacy_count_for_all_conversations if @conversations.limit_value || @conversations.offset_value || @conversations.eager_loading? + + counts = @conversations.unscope(:order).pick( + Arel.sql("COUNT(*) FILTER (WHERE assignee_id = #{current_user.id})"), + Arel.sql('COUNT(*) FILTER (WHERE assignee_id IS NULL)'), + Arel.sql('COUNT(*)') + ) + counts || [0, 0, 0] + end + + def legacy_count_for_all_conversations [ @conversations.assigned_to(current_user).count, @conversations.unassigned.count, diff --git a/app/javascript/dashboard/api/inboxes.js b/app/javascript/dashboard/api/inboxes.js index 114dbb6f4..3c1d17fc8 100644 --- a/app/javascript/dashboard/api/inboxes.js +++ b/app/javascript/dashboard/api/inboxes.js @@ -60,6 +60,12 @@ class Inboxes extends CacheEnabledApiClient { disableWhatsappCalling(inboxId) { return axios.post(`${this.url}/${inboxId}/disable_whatsapp_calling`); } + + setInboundCalls(inboxId, enabled) { + return axios.post(`${this.url}/${inboxId}/set_inbound_calls`, { + inbound_calls_enabled: enabled, + }); + } } export default new Inboxes(); diff --git a/app/javascript/dashboard/api/onboarding.js b/app/javascript/dashboard/api/onboarding.js index 2bc098eef..e15d16da3 100644 --- a/app/javascript/dashboard/api/onboarding.js +++ b/app/javascript/dashboard/api/onboarding.js @@ -9,6 +9,10 @@ class OnboardingAPI extends ApiClient { update(data) { return axios.patch(this.url, data); } + + getHelpCenterGeneration() { + return axios.get(`${this.url}/help_center_generation`); + } } export default new OnboardingAPI(); diff --git a/app/javascript/dashboard/components-next/Companies/CompaniesCard/CompaniesCard.vue b/app/javascript/dashboard/components-next/Companies/CompaniesCard/CompaniesCard.vue index fe4385bbb..d81b997c5 100644 --- a/app/javascript/dashboard/components-next/Companies/CompaniesCard/CompaniesCard.vue +++ b/app/javascript/dashboard/components-next/Companies/CompaniesCard/CompaniesCard.vue @@ -46,9 +46,8 @@ const formattedLastActivityAt = computed(() => { :src="avatarSource" class="shrink-0" :name="name" - :size="48" + :size="42" hide-offline-status - rounded-full />
diff --git a/app/javascript/dashboard/components-next/Companies/CompanyDetail/CompanyProfileCard.vue b/app/javascript/dashboard/components-next/Companies/CompanyDetail/CompanyProfileCard.vue index 1ffbf0c18..7615328ca 100644 --- a/app/javascript/dashboard/components-next/Companies/CompanyDetail/CompanyProfileCard.vue +++ b/app/javascript/dashboard/components-next/Companies/CompanyDetail/CompanyProfileCard.vue @@ -141,7 +141,6 @@ const handleUpdateCompany = async () => { :src="avatarSource" :size="72" :allow-upload="!isAvatarBusy" - rounded-full hide-offline-status @upload="handleAvatarUpload" @delete="handleAvatarDelete" diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue b/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue index ba887b46f..50af6f0c0 100644 --- a/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue +++ b/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue @@ -124,10 +124,9 @@ const handleAvatarHover = isHovered => {