diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index c429098564543..5420e829b39fe 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -25,13 +25,5 @@ jobs: with: issue-inactive-days: '30' issue-lock-reason: 'resolved' - issue-comment: > - This issue has been automatically locked since there - has not been any recent activity after it was closed. - Please open a new issue for related bugs. pr-inactive-days: '30' pr-lock-reason: 'resolved' - pr-comment: > - This pull request has been automatically locked since there - has not been any recent activity after it was closed. - Please open a new issue for related bugs. diff --git a/Gemfile b/Gemfile index e377866f7f5a3..ec57da19302f5 100644 --- a/Gemfile +++ b/Gemfile @@ -96,12 +96,12 @@ gem 'koala' # slack client gem 'slack-ruby-client', '~> 2.2.0' # for dialogflow integrations -gem 'google-cloud-dialogflow-v2' +gem 'google-cloud-dialogflow-v2', '>= 0.24.0' gem 'grpc' # Translate integrations # 'google-cloud-translate' gem depends on faraday 2.0 version # this dependency breaks the slack-ruby-client gem -gem 'google-cloud-translate-v3' +gem 'google-cloud-translate-v3', '>= 0.7.0' ##-- apm and error monitoring ---# # loaded only when environment variables are set. @@ -116,7 +116,7 @@ gem 'sentry-ruby', require: false gem 'sentry-sidekiq', '>= 5.19.0', require: false ##-- background job processing --## -gem 'sidekiq', '>= 7.3.0' +gem 'sidekiq', '>= 7.3.1' # We want cron jobs gem 'sidekiq-cron', '>= 1.12.0' @@ -165,7 +165,7 @@ gem 'audited', '~> 5.4', '>= 5.4.1' # need for google auth gem 'omniauth', '>= 2.1.2' -gem 'omniauth-google-oauth2', '>= 1.1.2' +gem 'omniauth-google-oauth2', '>= 1.1.3' gem 'omniauth-rails_csrf_protection', '~> 1.0', '>= 1.0.2' ## Gems for reponse bot @@ -200,7 +200,7 @@ group :development do gem 'rack-mini-profiler', '>= 3.2.0', require: false gem 'stackprof' # Should install the associated chrome extension to view query logs - gem 'meta_request', '>= 0.8.0' + gem 'meta_request', '>= 0.8.3' end group :test do @@ -228,7 +228,7 @@ group :development, :test do gem 'mock_redis' gem 'pry-rails' gem 'rspec_junit_formatter' - gem 'rspec-rails', '>= 6.1.3' + gem 'rspec-rails', '>= 6.1.5' gem 'rubocop', require: false gem 'rubocop-performance', require: false gem 'rubocop-rails', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 19f6e76c3e35a..70bee91706823 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -169,7 +169,7 @@ GEM climate_control (1.2.0) coderay (1.1.3) commonmarker (0.23.10) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) connection_pool (2.4.1) crack (1.0.0) bigdecimal @@ -230,7 +230,7 @@ GEM ruby2_keywords email_reply_trimmer (0.1.13) erubi (1.13.0) - et-orbi (1.2.7) + et-orbi (1.2.11) tzinfo execjs (2.8.1) facebook-messenger (2.0.1) @@ -257,7 +257,7 @@ GEM faraday-net_http_persistent (2.1.0) faraday (~> 2.5) net-http-persistent (~> 4.0) - faraday-retry (2.1.0) + faraday-retry (2.2.1) faraday (~> 2.0) fcm (1.0.8) faraday (>= 1.0.0, < 3.0) @@ -268,10 +268,10 @@ GEM rake flag_shih_tzu (0.3.23) foreman (0.87.2) - fugit (1.9.0) - et-orbi (~> 1, >= 1.2.7) + fugit (1.11.1) + et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) - gapic-common (0.18.0) + gapic-common (0.20.0) faraday (>= 1.9, < 3.a) faraday-retry (>= 1.0, < 3.a) google-protobuf (~> 3.14) @@ -301,15 +301,15 @@ GEM google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) - google-cloud-dialogflow-v2 (0.23.0) - gapic-common (>= 0.18.0, < 2.a) + google-cloud-dialogflow-v2 (0.31.0) + gapic-common (>= 0.20.0, < 2.a) google-cloud-errors (~> 1.0) google-cloud-location (>= 0.4, < 2.a) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) google-cloud-errors (1.3.1) - google-cloud-location (0.4.0) - gapic-common (>= 0.17.1, < 2.a) + google-cloud-location (0.6.0) + gapic-common (>= 0.20.0, < 2.a) google-cloud-errors (~> 1.0) google-cloud-storage (1.44.0) addressable (~> 2.8) @@ -319,17 +319,17 @@ GEM google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - google-cloud-translate-v3 (0.6.0) - gapic-common (>= 0.17.1, < 2.a) + google-cloud-translate-v3 (0.10.0) + gapic-common (>= 0.20.0, < 2.a) google-cloud-errors (~> 1.0) google-protobuf (3.25.3) google-protobuf (3.25.3-arm64-darwin) google-protobuf (3.25.3-x86_64-darwin) google-protobuf (3.25.3-x86_64-linux) - googleapis-common-protos (1.4.0) - google-protobuf (~> 3.14) - googleapis-common-protos-types (~> 1.2) - grpc (~> 1.27) + googleapis-common-protos (1.6.0) + google-protobuf (>= 3.18, < 5.a) + googleapis-common-protos-types (~> 1.7) + grpc (~> 1.41) googleapis-common-protos-types (1.14.0) google-protobuf (~> 3.18) googleauth (1.5.2) @@ -457,7 +457,7 @@ GEM marcel (1.0.4) maxminddb (0.1.22) memoist (0.16.2) - meta_request (0.8.2) + meta_request (0.8.3) rack-contrib (>= 1.1, < 3) railties (>= 3.0.0, < 8) method_source (1.1.0) @@ -467,7 +467,7 @@ GEM mini_magick (4.12.0) mini_mime (1.1.5) mini_portile2 (2.8.7) - minitest (5.24.1) + minitest (5.25.1) mock_redis (0.36.0) ruby2_keywords msgpack (1.7.0) @@ -480,7 +480,7 @@ GEM uri net-http-persistent (4.0.2) connection_pool (~> 2.2) - net-imap (0.4.12) + net-imap (0.4.14) date net-protocol net-pop (0.1.2) @@ -522,7 +522,7 @@ GEM hashie (>= 3.4.6) rack (>= 2.2.3) rack-protection - omniauth-google-oauth2 (1.1.2) + omniauth-google-oauth2 (1.1.3) jwt (>= 2.0) oauth2 (~> 2.0) omniauth (~> 2.0) @@ -634,20 +634,20 @@ GEM retriable (3.1.2) reverse_markdown (2.1.1) nokogiri - rexml (3.3.4) + rexml (3.3.6) strscan rspec-core (3.13.0) rspec-support (~> 3.13.0) - rspec-expectations (3.13.1) + rspec-expectations (3.13.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-mocks (3.13.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-rails (6.1.3) - actionpack (>= 6.1) - activesupport (>= 6.1) - railties (>= 6.1) + rspec-rails (7.0.1) + actionpack (>= 7.0) + activesupport (>= 7.0) + railties (>= 7.0) rspec-core (~> 3.13) rspec-expectations (~> 3.13) rspec-mocks (~> 3.13) @@ -722,7 +722,7 @@ GEM sexp_processor (4.17.0) shoulda-matchers (5.3.0) activesupport (>= 5.2.0) - sidekiq (7.3.0) + sidekiq (7.3.1) concurrent-ruby (< 2) connection_pool (>= 2.3.0) logger @@ -796,7 +796,7 @@ GEM uniform_notifier (1.16.0) uri (0.13.0) uri_template (0.7.0) - valid_email2 (4.0.6) + valid_email2 (5.2.6) activemodel (>= 3.2) mail (~> 2.5) version_gem (1.1.4) @@ -881,9 +881,9 @@ DEPENDENCIES foreman geocoder gmail_xoauth - google-cloud-dialogflow-v2 + google-cloud-dialogflow-v2 (>= 0.24.0) google-cloud-storage - google-cloud-translate-v3 + google-cloud-translate-v3 (>= 0.7.0) groupdate grpc haikunator @@ -903,14 +903,14 @@ DEPENDENCIES listen lograge (~> 0.14.0) maxminddb - meta_request (>= 0.8.0) + meta_request (>= 0.8.3) mock_redis neighbor net-smtp (~> 0.3.4) newrelic-sidekiq-metrics (>= 1.6.2) newrelic_rpm omniauth (>= 2.1.2) - omniauth-google-oauth2 (>= 1.1.2) + omniauth-google-oauth2 (>= 1.1.3) omniauth-oauth2 omniauth-rails_csrf_protection (~> 1.0, >= 1.0.2) pg @@ -930,7 +930,7 @@ DEPENDENCIES responders (>= 3.1.1) rest-client reverse_markdown - rspec-rails (>= 6.1.3) + rspec-rails (>= 6.1.5) rspec_junit_formatter rubocop rubocop-performance @@ -943,7 +943,7 @@ DEPENDENCIES sentry-ruby sentry-sidekiq (>= 5.19.0) shoulda-matchers - sidekiq (>= 7.3.0) + sidekiq (>= 7.3.1) sidekiq-cron (>= 1.12.0) simplecov (= 0.17.1) slack-ruby-client (~> 2.2.0) diff --git a/app/builders/account_builder.rb b/app/builders/account_builder.rb index f179c6405dfb7..6f79805865f22 100644 --- a/app/builders/account_builder.rb +++ b/app/builders/account_builder.rb @@ -32,11 +32,13 @@ def account_name end def validate_email + raise InvalidEmail.new({ domain_blocked: domain_blocked }) if domain_blocked? + address = ValidEmail2::Address.new(@email) - if address.valid? # && !address.disposable? + if address.valid? && !address.disposable? true else - raise InvalidEmail.new(valid: address.valid?) + raise InvalidEmail.new({ valid: address.valid?, disposable: address.disposable? }) end end @@ -79,4 +81,21 @@ def create_user @user.confirm if @confirmed @user.save! end + + def domain_blocked? + domain = @email.split('@').last + + blocked_domains.each do |blocked_domain| + return true if domain.match?(blocked_domain) + end + + false + end + + def blocked_domains + domains = GlobalConfigService.load('BLOCKED_EMAIL_DOMAINS', '') + return [] if domains.blank? + + domains.split("\n").map(&:strip) + end end diff --git a/app/controllers/api/v1/accounts/agents_controller.rb b/app/controllers/api/v1/accounts/agents_controller.rb index 4eff101275517..01aa42620b5ee 100644 --- a/app/controllers/api/v1/accounts/agents_controller.rb +++ b/app/controllers/api/v1/accounts/agents_controller.rb @@ -24,7 +24,7 @@ def create def update @agent.update!(agent_params.slice(:name).compact) - @agent.current_account_user.update!(agent_params.slice(:role, :availability, :auto_offline).compact) + @agent.current_account_user.update!(agent_params.slice(*account_user_attributes).compact) end def destroy @@ -67,8 +67,16 @@ def fetch_agent @agent = agents.find(params[:id]) end + def account_user_attributes + [:role, :availability, :auto_offline] + end + + def allowed_agent_params + [:name, :email, :name, :role, :availability, :auto_offline] + end + def agent_params - params.require(:agent).permit(:name, :email, :name, :role, :availability, :auto_offline) + params.require(:agent).permit(allowed_agent_params) end def new_agent_params @@ -101,3 +109,5 @@ def delete_user_record(agent) DeleteObjectJob.perform_later(agent) if agent.reload.account_users.blank? end end + +Api::V1::Accounts::AgentsController.prepend_mod_with('Api::V1::Accounts::AgentsController') diff --git a/app/controllers/api/v1/accounts/conversations/messages_controller.rb b/app/controllers/api/v1/accounts/conversations/messages_controller.rb index d34561e36589f..63226f342b0e3 100644 --- a/app/controllers/api/v1/accounts/conversations/messages_controller.rb +++ b/app/controllers/api/v1/accounts/conversations/messages_controller.rb @@ -13,7 +13,7 @@ def create def destroy ActiveRecord::Base.transaction do - message.update!(content: I18n.t('conversations.messages.deleted'), content_attributes: { deleted: true }) + message.update!(content: I18n.t('conversations.messages.deleted'), content_type: :text, content_attributes: { deleted: true }) message.attachments.destroy_all end end diff --git a/app/controllers/api/v1/accounts/integrations/hooks_controller.rb b/app/controllers/api/v1/accounts/integrations/hooks_controller.rb index d09ea2f00ef9d..087a9b78d3ea3 100644 --- a/app/controllers/api/v1/accounts/integrations/hooks_controller.rb +++ b/app/controllers/api/v1/accounts/integrations/hooks_controller.rb @@ -11,7 +11,17 @@ def update end def process_event - render json: { message: @hook.process_event(params[:event]) } + response = @hook.process_event(params[:event]) + + # for cases like an invalid event, or when conversation does not have enough messages + # for a label suggestion, the response is nil + if response.nil? + render json: { message: nil } + elsif response[:error] + render json: { error: response[:error] }, status: :unprocessable_entity + else + render json: { message: response[:message] } + end end def destroy diff --git a/app/controllers/api/v1/accounts/upload_controller.rb b/app/controllers/api/v1/accounts/upload_controller.rb index 4c54938b71e11..6530279da69b9 100644 --- a/app/controllers/api/v1/accounts/upload_controller.rb +++ b/app/controllers/api/v1/accounts/upload_controller.rb @@ -1,13 +1,68 @@ class Api::V1::Accounts::UploadController < Api::V1::Accounts::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 + result = if params[:attachment].present? + create_from_file + elsif params[:external_url].present? + create_from_url + else + render_error('No file or URL provided', :unprocessable_entity) + end + + render_success(result) if result.is_a?(ActiveStorage::Blob) + end + + private + + def create_from_file + attachment = params[:attachment] + create_and_save_blob(attachment.tempfile, attachment.original_filename, attachment.content_type) + end + + def create_from_url + uri = parse_uri(params[:external_url]) + return if performed? + + fetch_and_process_file_from_uri(uri) + end + + def parse_uri(url) + uri = URI.parse(url) + validate_uri(uri) + uri + rescue URI::InvalidURIError, SocketError + render_error('Invalid URL provided', :unprocessable_entity) + nil + end + + def validate_uri(uri) + raise URI::InvalidURIError unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS) + end + + def fetch_and_process_file_from_uri(uri) + uri.open do |file| + create_and_save_blob(file, File.basename(uri.path), file.content_type) + end + rescue OpenURI::HTTPError => e + render_error("Failed to fetch file from URL: #{e.message}", :unprocessable_entity) + rescue SocketError + render_error('Invalid URL provided', :unprocessable_entity) + rescue StandardError + render_error('An unexpected error occurred', :internal_server_error) + end + + def create_and_save_blob(io, filename, content_type) + ActiveStorage::Blob.create_and_upload!( + io: io, + filename: filename, + content_type: content_type ) - file_blob.save! + end + def render_success(file_blob) render json: { file_url: url_for(file_blob), blob_key: file_blob.key, blob_id: file_blob.id } end + + def render_error(message, status) + render json: { error: message }, status: status + end end diff --git a/app/controllers/api/v1/widget/conversations_controller.rb b/app/controllers/api/v1/widget/conversations_controller.rb index 7e6d84bd250d9..fe5facc1a8078 100644 --- a/app/controllers/api/v1/widget/conversations_controller.rb +++ b/app/controllers/api/v1/widget/conversations_controller.rb @@ -11,6 +11,8 @@ def create process_update_contact @conversation = create_conversation conversation.messages.create!(message_params) + # TODO: Temporary fix for message type cast issue, since message_type is returning as string instead of integer + conversation.reload end end diff --git a/app/controllers/api/v2/accounts/reports_controller.rb b/app/controllers/api/v2/accounts/reports_controller.rb index ed5be55181a7a..af3655e59a8b1 100644 --- a/app/controllers/api/v2/accounts/reports_controller.rb +++ b/app/controllers/api/v2/accounts/reports_controller.rb @@ -66,7 +66,9 @@ def generate_csv(filename, template) end def check_authorization - raise Pundit::NotAuthorizedError unless Current.account_user.administrator? + return if Current.account_user.administrator? + + raise Pundit::NotAuthorizedError end def common_params @@ -135,3 +137,5 @@ def conversation_metrics V2::ReportBuilder.new(Current.account, conversation_params).conversation_metrics end end + +Api::V2::Accounts::ReportsController.prepend_mod_with('Api::V2::Accounts::ReportsController') diff --git a/app/controllers/public/api/v1/portals/articles_controller.rb b/app/controllers/public/api/v1/portals/articles_controller.rb index 46ecf19a7f931..2807ae41a81b2 100644 --- a/app/controllers/public/api/v1/portals/articles_controller.rb +++ b/app/controllers/public/api/v1/portals/articles_controller.rb @@ -6,7 +6,7 @@ class Public::Api::V1::Portals::ArticlesController < Public::Api::V1::Portals::B layout 'portal' def index - @articles = @portal.articles + @articles = @portal.articles.published search_articles order_by_sort_param @articles.page(list_params[:page]) if list_params[:page].present? diff --git a/app/javascript/dashboard/api/customRole.js b/app/javascript/dashboard/api/customRole.js new file mode 100644 index 0000000000000..5074657d52061 --- /dev/null +++ b/app/javascript/dashboard/api/customRole.js @@ -0,0 +1,9 @@ +import ApiClient from './ApiClient'; + +class CustomRole extends ApiClient { + constructor() { + super('custom_roles', { accountScoped: true }); + } +} + +export default new CustomRole(); diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index fa95c5024f71a..5d3886a851899 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -4,6 +4,7 @@ import { mapGetters } from 'vuex'; import { useUISettings } from 'dashboard/composables/useUISettings'; import { useAlert } from 'dashboard/composables'; import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents'; +import { useFilter } from 'shared/composables/useFilter'; import VirtualList from 'vue-virtual-scroll-list'; import ChatListHeader from './ChatListHeader.vue'; @@ -16,7 +17,6 @@ import filterQueryGenerator from '../helper/filterQueryGenerator.js'; import AddCustomViews from 'dashboard/routes/dashboard/customviews/AddCustomViews.vue'; import DeleteCustomViews from 'dashboard/routes/dashboard/customviews/DeleteCustomViews.vue'; import ConversationBulkActions from './widgets/conversation/conversationBulkActions/Index.vue'; -import filterMixin from 'shared/mixins/filterMixin'; import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages'; import countries from 'shared/constants/countries'; import { generateValuesForEditCustomViews } from 'dashboard/helper/customViewsHelper'; @@ -27,6 +27,11 @@ import { } from '../store/modules/conversations/helpers/actionHelpers'; import { CONVERSATION_EVENTS } from '../helper/AnalyticsHelper/events'; import IntersectionObserver from './IntersectionObserver.vue'; +import { + getUserPermissions, + filterItemsByPermission, +} from 'dashboard/helper/permissionsHelper.js'; +import { ASSIGNEE_TYPE_TAB_PERMISSIONS } from 'dashboard/constants/permissions.js'; export default { components: { @@ -41,7 +46,6 @@ export default { IntersectionObserver, VirtualList, }, - mixins: [filterMixin], provide() { return { // Actions to be performed on virtual list item and context menu. @@ -91,6 +95,15 @@ export default { const conversationListRef = ref(null); + const { + setFilterAttributes, + initializeStatusAndAssigneeFilterToModal, + initializeInboxTeamAndLabelFilterToModal, + } = useFilter({ + filteri18nKey: 'FILTER', + attributeModel: 'conversation_attribute', + }); + const getKeyboardListenerParams = () => { const allConversations = conversationListRef.value.querySelectorAll( 'div.conversations-list div.conversation' @@ -109,43 +122,52 @@ export default { lastConversationIndex, }; }; - const handlePreviousConversation = () => { - const { allConversations, activeConversationIndex } = - getKeyboardListenerParams(); - if (activeConversationIndex === -1) { - allConversations[0].click(); - } - if (activeConversationIndex >= 1) { - allConversations[activeConversationIndex - 1].click(); - } - }; - const handleNextConversation = () => { + const handleConversationNavigation = direction => { const { allConversations, activeConversationIndex, lastConversationIndex, } = getKeyboardListenerParams(); - if (activeConversationIndex === -1) { - allConversations[lastConversationIndex].click(); - } else if (activeConversationIndex < lastConversationIndex) { - allConversations[activeConversationIndex + 1].click(); + + // Determine the new index based on the direction + const newIndex = + direction === 'previous' + ? activeConversationIndex - 1 + : activeConversationIndex + 1; + + // Check if the new index is within the valid range + if ( + allConversations.length > 0 && + newIndex >= 0 && + newIndex <= lastConversationIndex + ) { + // Click the conversation at the new index + allConversations[newIndex].click(); + } else if (allConversations.length > 0) { + // If the new index is out of range, click the first or last conversation based on the direction + const fallbackIndex = + direction === 'previous' ? 0 : lastConversationIndex; + allConversations[fallbackIndex].click(); } }; const keyboardEvents = { 'Alt+KeyJ': { - action: () => handlePreviousConversation(), + action: () => handleConversationNavigation('previous'), allowOnFocusedInput: true, }, 'Alt+KeyK': { - action: () => handleNextConversation(), + action: () => handleConversationNavigation('next'), allowOnFocusedInput: true, }, }; - useKeyboardEvents(keyboardEvents, conversationListRef); + useKeyboardEvents(keyboardEvents); return { uiSettings, conversationListRef, + setFilterAttributes, + initializeStatusAndAssigneeFilterToModal, + initializeInboxTeamAndLabelFilterToModal, }; }, data() { @@ -187,6 +209,7 @@ export default { computed: { ...mapGetters({ currentUser: 'getCurrentUser', + currentAccountId: 'getCurrentAccountId', chatLists: 'getAllConversations', mineChatsList: 'getMineChats', allChatList: 'getAllStatusChats', @@ -226,20 +249,19 @@ export default { name, }; }, + userPermissions() { + return getUserPermissions(this.currentUser, this.currentAccountId); + }, assigneeTabItems() { - const ASSIGNEE_TYPE_TAB_KEYS = { - me: 'mineCount', - unassigned: 'unAssignedCount', - all: 'allCount', - }; - return Object.keys(ASSIGNEE_TYPE_TAB_KEYS).map(key => { - const count = this.conversationStats[ASSIGNEE_TYPE_TAB_KEYS[key]] || 0; - return { - key, - name: this.$t(`CHAT_LIST.ASSIGNEE_TYPE_TABS.${key}`), - count, - }; - }); + return filterItemsByPermission( + ASSIGNEE_TYPE_TAB_PERMISSIONS, + this.userPermissions, + item => item.permissions + ).map(({ key, count: countKey }) => ({ + key, + name: this.$t(`CHAT_LIST.ASSIGNEE_TYPE_TABS.${key}`), + count: this.conversationStats[countKey] || 0, + })); }, showAssigneeInConversationCard() { return ( @@ -860,6 +882,25 @@ export default { onContextMenuToggle(state) { this.isContextMenuOpen = state; }, + initializeExistingFilterToModal() { + const statusFilter = this.initializeStatusAndAssigneeFilterToModal( + this.activeStatus, + this.currentUserDetails, + this.activeAssigneeTab + ); + if (statusFilter) { + this.appliedFilter.push(statusFilter); + } + + const otherFilters = this.initializeInboxTeamAndLabelFilterToModal( + this.conversationInbox, + this.inbox, + this.teamId, + this.activeTeam, + this.label + ); + this.appliedFilter.push(...otherFilters); + }, }, }; diff --git a/app/javascript/dashboard/components/CustomAttribute.vue b/app/javascript/dashboard/components/CustomAttribute.vue index 4bf97320bf6fc..ee00c182d4e08 100644 --- a/app/javascript/dashboard/components/CustomAttribute.vue +++ b/app/javascript/dashboard/components/CustomAttribute.vue @@ -331,10 +331,12 @@ export default { ::v-deep { .selector-wrap { @apply m-0 top-1; + .selector-name { @apply ml-0; } } + .name { @apply ml-0; } diff --git a/app/javascript/dashboard/components/buttons/ResolveAction.vue b/app/javascript/dashboard/components/buttons/ResolveAction.vue index 7c4c4daaa2343..0ff3638742f72 100644 --- a/app/javascript/dashboard/components/buttons/ResolveAction.vue +++ b/app/javascript/dashboard/components/buttons/ResolveAction.vue @@ -13,13 +13,12 @@ import wootConstants from 'dashboard/constants/globals'; import { CMD_REOPEN_CONVERSATION, CMD_RESOLVE_CONVERSATION, -} from 'dashboard/routes/dashboard/commands/commandBarBusEvents'; +} from 'dashboard/helper/commandbar/events'; const store = useStore(); const getters = useStoreGetters(); const { t } = useI18n(); -const resolveActionsRef = ref(null); const arrowDownButtonRef = ref(null); const isLoading = ref(false); @@ -131,17 +130,14 @@ const keyboardEvents = { }, }; -useKeyboardEvents(keyboardEvents, resolveActionsRef); +useKeyboardEvents(keyboardEvents); useEmitter(CMD_REOPEN_CONVERSATION, onCmdOpenConversation); useEmitter(CMD_RESOLVE_CONVERSATION, onCmdResolveConversation);