diff --git a/.github/workflows/publish_ee_docker.yml b/.github/workflows/publish_ee_docker.yml new file mode 100644 index 000000000..8e2c22481 --- /dev/null +++ b/.github/workflows/publish_ee_docker.yml @@ -0,0 +1,140 @@ +# # +# # This action will publish Chatwoot EE docker image. +# # This is set to run against merges to develop, master +# # and when tags are created. +# # + +name: Publish Chatwoot EE docker images + +on: + push: + branches: + - develop + - master + tags: + - v* + workflow_dispatch: + +env: + DOCKER_REPO: chatwoot/chatwoot + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-22.04-arm + runs-on: ${{ matrix.runner }} + env: + GIT_REF: ${{ github.head_ref || github.ref_name }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Prepare + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + + - name: Set Chatwoot edition + run: | + echo -en '\nENV CW_EDITION="ee"' >> docker/Dockerfile + + - name: Set Docker Tags + run: | + SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g') + if [ "${{ github.ref_name }}" = "master" ]; then + echo "DOCKER_TAG=${DOCKER_REPO}:latest" >> $GITHUB_ENV + else + echo "DOCKER_TAG=${DOCKER_REPO}:${SANITIZED_REF}" >> $GITHUB_ENV + fi + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 + with: + context: . + file: docker/Dockerfile + platforms: ${{ matrix.platform }} + push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} + outputs: type=image,name=${{ env.DOCKER_REPO }},push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + mkdir -p ${{ runner.temp }}/digests + digest="${{ steps.build.outputs.digest }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: + - build + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: ${{ runner.temp }}/digests + pattern: digests-* + merge-multiple: true + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Create manifest list and push + working-directory: ${{ runner.temp }}/digests + env: + GIT_REF: ${{ github.head_ref || github.ref_name }} + run: | + SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g') + if [ "${{ github.ref_name }}" = "master" ]; then + TAG="${DOCKER_REPO}:latest" + else + TAG="${DOCKER_REPO}:${SANITIZED_REF}" + fi + + docker buildx imagetools create -t $TAG \ + $(printf '${{ env.DOCKER_REPO }}@sha256:%s ' *) + + - name: Inspect image + env: + GIT_REF: ${{ github.head_ref || github.ref_name }} + run: | + SANITIZED_REF=$(echo "$GIT_REF" | sed 's/\//-/g') + if [ "${{ github.ref_name }}" = "master" ]; then + TAG="${DOCKER_REPO}:latest" + else + TAG="${DOCKER_REPO}:${SANITIZED_REF}" + fi + + docker buildx imagetools inspect $TAG diff --git a/.github/workflows/test_docker_build.yml b/.github/workflows/test_docker_build.yml new file mode 100644 index 000000000..460c4ba1f --- /dev/null +++ b/.github/workflows/test_docker_build.yml @@ -0,0 +1,40 @@ +name: Test Docker Build + +on: + pull_request: + branches: + - develop + - master + workflow_dispatch: + +jobs: + test-build: + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-22.04-arm + runs-on: ${{ matrix.runner }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: docker/Dockerfile + platforms: ${{ matrix.platform }} + push: false + load: false + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.husky/pre-commit b/.husky/pre-commit index 2525dde4e..adda426ad 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,11 +1,11 @@ -# #!/bin/sh -# . "$(dirname "$0")/_/husky.sh" +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" -# # lint js and vue files -# npx --no-install lint-staged +# lint js and vue files +npx --no-install lint-staged -# # lint only staged ruby files -# git diff --name-only --cached | xargs ls -1 2>/dev/null | grep '\.rb$' | xargs bundle exec rubocop --force-exclusion -a +# lint only staged ruby files +git diff --name-only --cached | xargs ls -1 2>/dev/null | grep '\.rb$' | xargs bundle exec rubocop --force-exclusion -a -# # stage rubocop changes to files -# git diff --name-only --cached | xargs git add +# stage rubocop changes to files +git diff --name-only --cached | xargs git add diff --git a/Gemfile b/Gemfile index 45e194e13..937aef4af 100644 --- a/Gemfile +++ b/Gemfile @@ -94,7 +94,7 @@ gem 'twitty', '~> 0.1.5' # facebook client gem 'koala' # slack client -gem 'slack-ruby-client', '~> 2.2.0' +gem 'slack-ruby-client', '~> 2.5.2' # for dialogflow integrations gem 'google-cloud-dialogflow-v2', '>= 0.24.0' gem 'grpc' @@ -138,9 +138,7 @@ gem 'procore-sift' # parse email gem 'email_reply_trimmer' -# TODO: we might have to fork this gem since 0.3.1 has hard depency on nokogir 1.10. -# and this gem hasn't been updated for a while. -gem 'html2text', git: 'https://github.com/chatwoot/html2text_ruby', branch: 'chatwoot' +gem 'html2text' # to calculate working hours gem 'working_hours' diff --git a/Gemfile.lock b/Gemfile.lock index fa45d1e89..e9a573816 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -22,14 +22,6 @@ GIT devise (>= 4.0.0, < 5.0.0) railties (>= 5.0.0, < 8.0.0) -GIT - remote: https://github.com/chatwoot/html2text_ruby - revision: cdbdbbbf898d846d0136d69d688a003c6b26074b - branch: chatwoot - specs: - html2text (0.3.1) - nokogiri (>= 1.13.6) - GEM remote: https://rubygems.org/ specs: @@ -280,7 +272,8 @@ GEM googleauth (~> 1.0) grpc (~> 1.36) geocoder (1.8.1) - gli (2.21.1) + gli (2.22.2) + ostruct globalid (1.2.1) activesupport (>= 6.1) gmail_xoauth (0.4.3) @@ -361,6 +354,8 @@ GEM hana (1.3.7) hashdiff (1.1.0) hashie (5.0.0) + html2text (0.4.0) + nokogiri (>= 1.0, < 2.0) http (5.1.1) addressable (~> 2.8) http-cookie (~> 1.0) @@ -503,14 +498,14 @@ GEM newrelic_rpm (9.6.0) base64 nio4r (2.7.3) - nokogiri (1.17.1) + nokogiri (1.18.3) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.17.1-arm64-darwin) + nokogiri (1.18.3-arm64-darwin) racc (~> 1.4) - nokogiri (1.17.1-x86_64-darwin) + nokogiri (1.18.3-x86_64-darwin) racc (~> 1.4) - nokogiri (1.17.1-x86_64-linux) + nokogiri (1.18.3-x86_64-linux-gnu) racc (~> 1.4) oauth (1.1.0) oauth-tty (~> 1.0, >= 1.0.1) @@ -543,6 +538,7 @@ GEM openssl (3.2.0) orm_adapter (0.5.0) os (1.1.4) + ostruct (0.6.1) parallel (1.23.0) parser (3.2.2.1) ast (~> 2.4.1) @@ -565,7 +561,7 @@ GEM activesupport (>= 3.0.0) raabro (1.4.0) racc (1.8.1) - rack (2.2.10) + rack (2.2.11) rack-attack (6.7.0) rack (>= 1.0, < 4) rack-contrib (2.5.0) @@ -751,12 +747,13 @@ GEM json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) - slack-ruby-client (2.2.0) + slack-ruby-client (2.5.2) faraday (>= 2.0) faraday-mashify faraday-multipart gli hashie + logger snaky_hash (2.0.1) hashie version_gem (~> 1.1, >= 1.1.1) @@ -897,7 +894,7 @@ DEPENDENCIES haikunator hairtrigger hashie - html2text! + html2text image_processing jbuilder json_refs @@ -957,7 +954,7 @@ DEPENDENCIES sidekiq (>= 7.3.1) sidekiq-cron (>= 1.12.0) simplecov (= 0.17.1) - slack-ruby-client (~> 2.2.0) + slack-ruby-client (~> 2.5.2) spring spring-watcher-listen squasher diff --git a/app/controllers/api/v1/accounts/inbox_members_controller.rb b/app/controllers/api/v1/accounts/inbox_members_controller.rb index 0cb1a0c85..bf8822c27 100644 --- a/app/controllers/api/v1/accounts/inbox_members_controller.rb +++ b/app/controllers/api/v1/accounts/inbox_members_controller.rb @@ -10,7 +10,7 @@ class Api::V1::Accounts::InboxMembersController < Api::V1::Accounts::BaseControl def create authorize @inbox, :create? ActiveRecord::Base.transaction do - agents_to_be_added_ids.map { |user_id| @inbox.add_member(user_id) } + @inbox.add_members(agents_to_be_added_ids) end fetch_updated_agents end @@ -24,7 +24,7 @@ class Api::V1::Accounts::InboxMembersController < Api::V1::Accounts::BaseControl def destroy authorize @inbox, :destroy? ActiveRecord::Base.transaction do - params[:user_ids].map { |user_id| @inbox.remove_member(user_id) } + @inbox.remove_members(params[:user_ids]) end head :ok end @@ -41,8 +41,8 @@ class Api::V1::Accounts::InboxMembersController < Api::V1::Accounts::BaseControl # the missing ones are the agents which are to be deleted from the inbox # the new ones are the agents which are to be added to the inbox ActiveRecord::Base.transaction do - agents_to_be_added_ids.each { |user_id| @inbox.add_member(user_id) } - agents_to_be_removed_ids.each { |user_id| @inbox.remove_member(user_id) } + @inbox.add_members(agents_to_be_added_ids) + @inbox.remove_members(agents_to_be_removed_ids) end end diff --git a/app/controllers/api/v1/accounts/team_members_controller.rb b/app/controllers/api/v1/accounts/team_members_controller.rb index 0243a915a..ffb115a34 100644 --- a/app/controllers/api/v1/accounts/team_members_controller.rb +++ b/app/controllers/api/v1/accounts/team_members_controller.rb @@ -9,14 +9,14 @@ class Api::V1::Accounts::TeamMembersController < Api::V1::Accounts::BaseControll def create ActiveRecord::Base.transaction do - @team_members = members_to_be_added_ids.map { |user_id| @team.add_member(user_id) } + @team_members = @team.add_members(members_to_be_added_ids) end end def update ActiveRecord::Base.transaction do - members_to_be_added_ids.each { |user_id| @team.add_member(user_id) } - members_to_be_removed_ids.each { |user_id| @team.remove_member(user_id) } + @team.add_members(members_to_be_added_ids) + @team.remove_members(members_to_be_removed_ids) end @team_members = @team.members render action: 'create' @@ -24,7 +24,7 @@ class Api::V1::Accounts::TeamMembersController < Api::V1::Accounts::BaseControll def destroy ActiveRecord::Base.transaction do - params[:user_ids].map { |user_id| @team.remove_member(user_id) } + @team.remove_members(params[:user_ids]) end head :ok end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 6ddaab73b..0f915ab8a 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -7,13 +7,17 @@ class DashboardController < ActionController::Base around_action :switch_locale before_action :ensure_installation_onboarding, only: [:index] before_action :render_hc_if_custom_domain, only: [:index] - + before_action :ensure_html_format layout 'vueapp' def index; end private + def ensure_html_format + head :not_acceptable unless request.format.html? + end + def set_global_config @global_config = GlobalConfig.get( 'LOGO', 'LOGO_DARK', 'LOGO_THUMBNAIL', @@ -32,7 +36,7 @@ class DashboardController < ActionController::Base 'LOGOUT_REDIRECT_LINK', 'DISABLE_USER_PROFILE_UPDATE', 'DEPLOYMENT_ENV', - 'CSML_EDITOR_HOST' + 'CSML_EDITOR_HOST', 'INSTALLATION_PRICING_PLAN' ).merge(app_config) end diff --git a/app/helpers/portal_helper.rb b/app/helpers/portal_helper.rb index 342bb62aa..2b1ab7b21 100644 --- a/app/helpers/portal_helper.rb +++ b/app/helpers/portal_helper.rb @@ -5,8 +5,7 @@ module PortalHelper end def generate_portal_bg(portal_color, theme) - bg_image = theme == 'dark' ? 'hexagon-dark.svg' : 'hexagon-light.svg' - "url(/assets/images/hc/#{bg_image}) #{generate_portal_bg_color(portal_color, theme)}" + generate_portal_bg_color(portal_color, theme) end def generate_gradient_to_bottom(theme) diff --git a/app/helpers/super_admin/account_features_helper.rb b/app/helpers/super_admin/account_features_helper.rb index 759aec134..c24e65e68 100644 --- a/app/helpers/super_admin/account_features_helper.rb +++ b/app/helpers/super_admin/account_features_helper.rb @@ -6,4 +6,47 @@ module SuperAdmin::AccountFeaturesHelper def self.account_premium_features account_features.filter { |feature| feature['premium'] }.pluck('name') end + + # Returns a hash mapping feature names to their display names + def self.feature_display_names + account_features.each_with_object({}) do |feature, hash| + hash[feature['name']] = feature['display_name'] + end + end + + def self.filter_internal_features(features) + return features if GlobalConfig.get_value('DEPLOYMENT_ENV') == 'cloud' + + internal_features = account_features.select { |f| f['chatwoot_internal'] }.pluck('name') + features.except(*internal_features) + end + + def self.filter_deprecated_features(features) + deprecated_features = account_features.select { |f| f['deprecated'] }.pluck('name') + features.except(*deprecated_features) + end + + def self.sort_and_transform_features(features, display_names) + features.sort_by { |key, _| display_names[key] || key } + .to_h + .transform_keys { |key| [key, display_names[key]] } + end + + def self.partition_features(features) + filtered = filter_internal_features(features) + filtered = filter_deprecated_features(filtered) + display_names = feature_display_names + + regular, premium = filtered.partition { |key, _value| account_premium_features.exclude?(key) } + + [ + sort_and_transform_features(regular, display_names), + sort_and_transform_features(premium, display_names) + ] + end + + def self.filtered_features(features) + regular, premium = partition_features(features) + regular.merge(premium) + end end diff --git a/app/javascript/dashboard/assets/scss/super_admin/index.scss b/app/javascript/dashboard/assets/scss/super_admin/index.scss index 91f7835d0..ddac5f7c8 100644 --- a/app/javascript/dashboard/assets/scss/super_admin/index.scss +++ b/app/javascript/dashboard/assets/scss/super_admin/index.scss @@ -6,3 +6,209 @@ body { font-family: Inter, -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important; } + +@layer base { + // FIXME: Use a common color file for all packs + // scss-lint:disable PropertySortOrder + :root { + --slate-1: 252 252 253; + --slate-2: 249 249 251; + --slate-3: 240 240 243; + --slate-4: 232 232 236; + --slate-5: 224 225 230; + --slate-6: 217 217 224; + --slate-7: 205 206 214; + --slate-8: 185 187 198; + --slate-9: 139 141 152; + --slate-10: 128 131 141; + --slate-11: 96 100 108; + --slate-12: 28 32 36; + + --iris-1: 253 253 255; + --iris-2: 248 248 255; + --iris-3: 240 241 254; + --iris-4: 230 231 255; + --iris-5: 218 220 255; + --iris-6: 203 205 255; + --iris-7: 184 186 248; + --iris-8: 155 158 240; + --iris-9: 91 91 214; + --iris-10: 81 81 205; + --iris-11: 87 83 198; + --iris-12: 39 41 98; + + --ruby-1: 255 252 253; + --ruby-2: 255 247 248; + --ruby-3: 254 234 237; + --ruby-4: 255 220 225; + --ruby-5: 255 206 214; + --ruby-6: 248 191 200; + --ruby-7: 239 172 184; + --ruby-8: 229 146 163; + --ruby-9: 229 70 102; + --ruby-10: 220 59 93; + --ruby-11: 202 36 77; + --ruby-12: 100 23 43; + + --amber-1: 254 253 251; + --amber-2: 254 251 233; + --amber-3: 255 247 194; + --amber-4: 255 238 156; + --amber-5: 251 229 119; + --amber-6: 243 214 115; + --amber-7: 233 193 98; + --amber-8: 226 163 54; + --amber-9: 255 197 61; + --amber-10: 255 186 24; + --amber-11: 171 100 0; + --amber-12: 79 52 34; + + --teal-1: 250 254 253; + --teal-2: 243 251 249; + --teal-3: 224 248 243; + --teal-4: 204 243 234; + --teal-5: 184 234 224; + --teal-6: 161 222 210; + --teal-7: 131 205 193; + --teal-8: 83 185 171; + --teal-9: 18 165 148; + --teal-10: 13 155 138; + --teal-11: 0 133 115; + --teal-12: 13 61 56; + + --gray-1: 252 252 252; + --gray-2: 249 249 249; + --gray-3: 240 240 240; + --gray-4: 232 232 232; + --gray-5: 224 224 224; + --gray-6: 217 217 217; + --gray-7: 206 206 206; + --gray-8: 187 187 187; + --gray-9: 141 141 141; + --gray-10: 131 131 131; + --gray-11: 100 100 100; + --gray-12: 32 32 32; + + --background-color: 253 253 253; + --text-blue: 8 109 224; + --border-container: 236 236 236; + --border-strong: 235 235 235; + --border-weak: 234 234 234; + --solid-1: 255 255 255; + --solid-2: 255 255 255; + --solid-3: 255 255 255; + --solid-active: 255 255 255; + --solid-amber: 252 232 193; + --solid-blue: 218 236 255; + --solid-iris: 230 231 255; + + --alpha-1: 67, 67, 67, 0.06; + --alpha-2: 201, 202, 207, 0.15; + --alpha-3: 255, 255, 255, 0.96; + --black-alpha-1: 0, 0, 0, 0.12; + --black-alpha-2: 0, 0, 0, 0.04; + --border-blue: 39, 129, 246, 0.5; + --white-alpha: 255, 255, 255, 0.8; + } + + .dark { + --slate-1: 17 17 19; + --slate-2: 24 25 27; + --slate-3: 33 34 37; + --slate-4: 39 42 45; + --slate-5: 46 49 53; + --slate-6: 54 58 63; + --slate-7: 67 72 78; + --slate-8: 90 97 105; + --slate-9: 105 110 119; + --slate-10: 119 123 132; + --slate-11: 176 180 186; + --slate-12: 237 238 240; + + --iris-1: 19 19 30; + --iris-2: 23 22 37; + --iris-3: 32 34 72; + --iris-4: 38 42 101; + --iris-5: 48 51 116; + --iris-6: 61 62 130; + --iris-7: 74 74 149; + --iris-8: 89 88 177; + --iris-9: 91 91 214; + --iris-10: 84 114 228; + --iris-11: 158 177 255; + --iris-12: 224 223 254; + + --ruby-1: 25 17 19; + --ruby-2: 30 21 23; + --ruby-3: 58 20 30; + --ruby-4: 78 19 37; + --ruby-5: 94 26 46; + --ruby-6: 111 37 57; + --ruby-7: 136 52 71; + --ruby-8: 179 68 90; + --ruby-9: 229 70 102; + --ruby-10: 236 90 114; + --ruby-11: 255 148 157; + --ruby-12: 254 210 225; + + --amber-1: 22 18 12; + --amber-2: 29 24 15; + --amber-3: 48 32 8; + --amber-4: 63 39 0; + --amber-5: 77 48 0; + --amber-6: 92 61 5; + --amber-7: 113 79 25; + --amber-8: 143 100 36; + --amber-9: 255 197 61; + --amber-10: 255 214 10; + --amber-11: 255 202 22; + --amber-12: 255 231 179; + + --teal-1: 13 21 20; + --teal-2: 17 28 27; + --teal-3: 13 45 42; + --teal-4: 2 59 55; + --teal-5: 8 72 67; + --teal-6: 20 87 80; + --teal-7: 28 105 97; + --teal-8: 32 126 115; + --teal-9: 18 165 148; + --teal-10: 14 179 158; + --teal-11: 11 216 182; + --teal-12: 173 240 221; + + --gray-1: 17 17 17; + --gray-2: 25 25 25; + --gray-3: 34 34 34; + --gray-4: 42 42 42; + --gray-5: 49 49 49; + --gray-6: 58 58 58; + --gray-7: 72 72 72; + --gray-8: 96 96 96; + --gray-9: 110 110 110; + --gray-10: 123 123 123; + --gray-11: 180 180 180; + --gray-12: 238 238 238; + + --background-color: 18 18 19; + --border-strong: 52 52 52; + --border-weak: 38 38 42; + --solid-1: 23 23 26; + --solid-2: 29 30 36; + --solid-3: 44 45 54; + --solid-active: 53 57 66; + --solid-amber: 42 37 30; + --solid-blue: 16 49 91; + --solid-iris: 38 42 101; + --text-blue: 126 182 255; + + --alpha-1: 36, 36, 36, 0.8; + --alpha-2: 139, 147, 182, 0.15; + --alpha-3: 36, 38, 45, 0.9; + --black-alpha-1: 0, 0, 0, 0.3; + --black-alpha-2: 0, 0, 0, 0.2; + --border-blue: 39, 129, 246, 0.5; + --border-container: 236, 236, 236, 0; + --white-alpha: 255, 255, 255, 0.1; + } +} diff --git a/app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/LiveChatCampaign/LiveChatCampaignDialog.vue b/app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/LiveChatCampaign/LiveChatCampaignDialog.vue index 2c4b9848c..ea76c6130 100644 --- a/app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/LiveChatCampaign/LiveChatCampaignDialog.vue +++ b/app/javascript/dashboard/components-next/Campaigns/Pages/CampaignPage/LiveChatCampaign/LiveChatCampaignDialog.vue @@ -21,11 +21,11 @@ const addCampaign = async campaignDetails => { type: CAMPAIGN_TYPES.ONGOING, }); - useAlert(t('CAMPAIGN.SMS.CREATE.FORM.API.SUCCESS_MESSAGE')); + useAlert(t('CAMPAIGN.LIVE_CHAT.CREATE.FORM.API.SUCCESS_MESSAGE')); } catch (error) { const errorMessage = error?.response?.message || - t('CAMPAIGN.SMS.CREATE.FORM.API.ERROR_MESSAGE'); + t('CAMPAIGN.LIVE_CHAT.CREATE.FORM.API.ERROR_MESSAGE'); useAlert(errorMessage); } }; diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsDetailsLayout.vue b/app/javascript/dashboard/components-next/Contacts/ContactsDetailsLayout.vue index ad007af1c..57c4ba7c8 100644 --- a/app/javascript/dashboard/components-next/Contacts/ContactsDetailsLayout.vue +++ b/app/javascript/dashboard/components-next/Contacts/ContactsDetailsLayout.vue @@ -8,17 +8,17 @@ import Breadcrumb from 'dashboard/components-next/breadcrumb/Breadcrumb.vue'; import ComposeConversation from 'dashboard/components-next/NewConversation/ComposeConversation.vue'; const props = defineProps({ - buttonLabel: { - type: String, - default: '', - }, selectedContact: { type: Object, default: () => ({}), }, + isUpdating: { + type: Boolean, + default: false, + }, }); -const emit = defineEmits(['goToContactsList']); +const emit = defineEmits(['goToContactsList', 'toggleBlock']); const { t } = useI18n(); const slots = useSlots(); @@ -45,9 +45,17 @@ const breadcrumbItems = computed(() => { return items; }); +const isContactBlocked = computed(() => { + return props.selectedContact?.blocked; +}); + const handleBreadcrumbClick = () => { emit('goToContactsList'); }; + +const toggleBlock = () => { + emit('toggleBlock', isContactBlocked.value); +};