From e863a52262abb18f40552447cdeb82888c1aef99 Mon Sep 17 00:00:00 2001 From: Pranav Date: Sun, 31 Aug 2025 07:01:41 -0700 Subject: [PATCH 01/94] chore: Update account deletion email copy (#12317) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the email copies for account deletion emails ## Manual Deletion Screenshot 2025-08-29 at 2 41 40 PM ## Inactive Deletion image --------- Co-authored-by: Cursor Agent Co-authored-by: Sojan Jose --- .../account_notification_mailer.rb | 28 +++- .../account_deletion.liquid | 16 --- .../account_deletion_for_inactivity.liquid | 21 +++ .../account_deletion_user_initiated.liquid | 16 +++ enterprise/app/models/enterprise/account.rb | 16 ++- spec/enterprise/models/account_spec.rb | 19 ++- .../account_notification_mailer_spec.rb | 126 ++++-------------- 7 files changed, 118 insertions(+), 124 deletions(-) delete mode 100644 app/views/mailers/administrator_notifications/account_notification_mailer/account_deletion.liquid create mode 100644 app/views/mailers/administrator_notifications/account_notification_mailer/account_deletion_for_inactivity.liquid create mode 100644 app/views/mailers/administrator_notifications/account_notification_mailer/account_deletion_user_initiated.liquid diff --git a/app/mailers/administrator_notifications/account_notification_mailer.rb b/app/mailers/administrator_notifications/account_notification_mailer.rb index 8837e4f8c..5ac753372 100644 --- a/app/mailers/administrator_notifications/account_notification_mailer.rb +++ b/app/mailers/administrator_notifications/account_notification_mailer.rb @@ -1,10 +1,22 @@ class AdministratorNotifications::AccountNotificationMailer < AdministratorNotifications::BaseMailer - def account_deletion(account, reason = 'manual_deletion') - subject = 'Your account has been marked for deletion' + def account_deletion_user_initiated(account, reason) + subject = 'Your Chatwoot account deletion has been scheduled' action_url = settings_url('general') meta = { 'account_name' => account.name, - 'deletion_date' => account.custom_attributes['marked_for_deletion_at'], + 'deletion_date' => format_deletion_date(account.custom_attributes['marked_for_deletion_at']), + 'reason' => reason + } + + send_notification(subject, action_url: action_url, meta: meta) + end + + def account_deletion_for_inactivity(account, reason) + subject = 'Your Chatwoot account is scheduled for deletion due to inactivity' + action_url = settings_url('general') + meta = { + 'account_name' => account.name, + 'deletion_date' => format_deletion_date(account.custom_attributes['marked_for_deletion_at']), 'reason' => reason } @@ -45,4 +57,14 @@ class AdministratorNotifications::AccountNotificationMailer < AdministratorNotif send_notification(subject, action_url: action_url, meta: meta) end + + private + + def format_deletion_date(deletion_date_str) + return 'Unknown' if deletion_date_str.blank? + + Time.zone.parse(deletion_date_str).strftime('%B %d, %Y') + rescue StandardError + 'Unknown' + end end diff --git a/app/views/mailers/administrator_notifications/account_notification_mailer/account_deletion.liquid b/app/views/mailers/administrator_notifications/account_notification_mailer/account_deletion.liquid deleted file mode 100644 index 0873dbac1..000000000 --- a/app/views/mailers/administrator_notifications/account_notification_mailer/account_deletion.liquid +++ /dev/null @@ -1,16 +0,0 @@ -

Hello,

- -

Your account {{ meta.account_name }} has been marked for deletion. The account will be permanently deleted on {{ meta.deletion_date }}.

- -{% if meta.reason == 'manual_deletion' %} -

This action was requested by one of the administrators of your account.

-{% else %} -

Reason for deletion: {{ meta.reason }}

-{% endif %} - -

If this was done in error, you can cancel the deletion process by visiting your account settings.

- -

Cancel Account Deletion

- -

Thank you,
-Team Chatwoot

\ No newline at end of file diff --git a/app/views/mailers/administrator_notifications/account_notification_mailer/account_deletion_for_inactivity.liquid b/app/views/mailers/administrator_notifications/account_notification_mailer/account_deletion_for_inactivity.liquid new file mode 100644 index 000000000..273eca12b --- /dev/null +++ b/app/views/mailers/administrator_notifications/account_notification_mailer/account_deletion_for_inactivity.liquid @@ -0,0 +1,21 @@ +

Hello there,

+ +

We've noticed that your Chatwoot account {{ meta.account_name }} has been inactive for some time. Because of this, it's scheduled for deletion on {{ meta.deletion_date }}.

+ +

How do I keep my account?

+ +

Log in to your Chatwoot account before {{ meta.deletion_date }}. From your account settings, you can cancel the deletion and continue using your account.

+ +

What happens if I don't cancel?

+ +

Unless you cancel the account deletion before {{ meta.deletion_date }}, your account and all associated data — including conversations, contacts, reports, and settings — will be permanently deleted.

+ +

Why are we doing this?

+ +

To keep things secure and efficient, we regularly remove inactive accounts so our systems remain optimized for active teams.

+ +

If you have any questions, feel free to reach us at hello@chatwoot.com.

+ +

— The Chatwoot Team

+ + diff --git a/app/views/mailers/administrator_notifications/account_notification_mailer/account_deletion_user_initiated.liquid b/app/views/mailers/administrator_notifications/account_notification_mailer/account_deletion_user_initiated.liquid new file mode 100644 index 000000000..2039a93df --- /dev/null +++ b/app/views/mailers/administrator_notifications/account_notification_mailer/account_deletion_user_initiated.liquid @@ -0,0 +1,16 @@ +

Hello there,

+ +

An account administrator has requested deletion of the Chatwoot account {{ meta.account_name }}. The account is scheduled for deletion on {{ meta.deletion_date }}.

+ +

What happens next?

+ +
    +
  • The account will remain accessible until the scheduled deletion date.
  • +
  • After that, all data including conversations, contacts, integrations, and settings will be permanently removed.
  • +
+ +

If you change your mind before the deletion date, you can cancel this request by visiting your account settings.

+ +

— The Chatwoot Team

+ + diff --git a/enterprise/app/models/enterprise/account.rb b/enterprise/app/models/enterprise/account.rb index fae44dab6..dff97c0ee 100644 --- a/enterprise/app/models/enterprise/account.rb +++ b/enterprise/app/models/enterprise/account.rb @@ -4,10 +4,22 @@ module Enterprise::Account def manually_managed_features; end def mark_for_deletion(reason = 'manual_deletion') - result = custom_attributes.merge!('marked_for_deletion_at' => 7.days.from_now.iso8601, 'marked_for_deletion_reason' => reason) && save + reason = reason.to_s == 'manual_deletion' ? 'manual_deletion' : 'inactivity' + + result = custom_attributes.merge!( + 'marked_for_deletion_at' => 7.days.from_now.iso8601, + 'marked_for_deletion_reason' => reason + ) && save # Send notification to admin users if the account was successfully marked for deletion - AdministratorNotifications::AccountNotificationMailer.with(account: self).account_deletion(self, reason).deliver_later if result + if result + mailer = AdministratorNotifications::AccountNotificationMailer.with(account: self) + if reason == 'manual_deletion' + mailer.account_deletion_user_initiated(self, reason).deliver_later + else + mailer.account_deletion_for_inactivity(self, reason).deliver_later + end + end result end diff --git a/spec/enterprise/models/account_spec.rb b/spec/enterprise/models/account_spec.rb index 4d851d50e..1c727328e 100644 --- a/spec/enterprise/models/account_spec.rb +++ b/spec/enterprise/models/account_spec.rb @@ -229,18 +229,27 @@ RSpec.describe Account, type: :model do describe '#mark_for_deletion' do it 'sets the marked_for_deletion_at and marked_for_deletion_reason attributes' do expect do - account.mark_for_deletion('test_reason') + account.mark_for_deletion('inactivity') end.to change { account.reload.custom_attributes['marked_for_deletion_at'] }.from(nil).to(be_present) - .and change { account.reload.custom_attributes['marked_for_deletion_reason'] }.from(nil).to('test_reason') + .and change { account.reload.custom_attributes['marked_for_deletion_reason'] }.from(nil).to('inactivity') end - it 'sends a notification email to admin users' do + it 'sends a user-initiated deletion email when reason is manual_deletion' do mailer = double expect(AdministratorNotifications::AccountNotificationMailer).to receive(:with).with(account: account).and_return(mailer) - expect(mailer).to receive(:account_deletion).with(account, 'test_reason').and_return(mailer) + expect(mailer).to receive(:account_deletion_user_initiated).with(account, 'manual_deletion').and_return(mailer) expect(mailer).to receive(:deliver_later) - account.mark_for_deletion('test_reason') + account.mark_for_deletion('manual_deletion') + end + + it 'sends a system-initiated deletion email when reason is not manual_deletion' do + mailer = double + expect(AdministratorNotifications::AccountNotificationMailer).to receive(:with).with(account: account).and_return(mailer) + expect(mailer).to receive(:account_deletion_for_inactivity).with(account, 'inactivity').and_return(mailer) + expect(mailer).to receive(:deliver_later) + + account.mark_for_deletion('inactivity') end it 'returns true when successful' do diff --git a/spec/mailers/administrator_notifications/account_notification_mailer_spec.rb b/spec/mailers/administrator_notifications/account_notification_mailer_spec.rb index 44df38e88..dbe6ac11b 100644 --- a/spec/mailers/administrator_notifications/account_notification_mailer_spec.rb +++ b/spec/mailers/administrator_notifications/account_notification_mailer_spec.rb @@ -1,116 +1,46 @@ require 'rails_helper' -require Rails.root.join 'spec/mailers/administrator_notifications/shared/smtp_config_shared.rb' RSpec.describe AdministratorNotifications::AccountNotificationMailer do - include_context 'with smtp config' + let(:account) { create(:account, name: 'Test Account') } + let(:mailer) { described_class.with(account: account) } + let(:class_instance) { described_class.new } - let!(:account) { create(:account) } - let!(:admin) { create(:user, account: account, role: :administrator) } + before do + allow(described_class).to receive(:new).and_return(class_instance) + allow(class_instance).to receive(:smtp_config_set_or_development?).and_return(true) + account.custom_attributes['marked_for_deletion_at'] = 7.days.from_now.iso8601 + account.save! + end - describe 'account_deletion' do - let(:reason) { 'manual_deletion' } - let(:mail) { described_class.with(account: account).account_deletion(account, reason) } - let(:deletion_date) { 7.days.from_now.iso8601 } - - before do - account.update!(custom_attributes: { - 'marked_for_deletion_at' => deletion_date, - 'marked_for_deletion_reason' => reason - }) - end - - it 'renders the subject' do - expect(mail.subject).to eq('Your account has been marked for deletion') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([admin.email]) - end - - it 'includes the account name in the email body' do - expect(mail.body.encoded).to include(account.name) - end - - it 'includes the deletion date in the email body' do - expect(mail.body.encoded).to include(deletion_date) - end - - it 'includes a link to cancel the deletion' do - expect(mail.body.encoded).to include('Cancel Account Deletion') - end - - context 'when reason is manual_deletion' do - it 'includes the administrator message' do - expect(mail.body.encoded).to include('This action was requested by one of the administrators of your account') - end - end - - context 'when reason is not manual_deletion' do - let(:reason) { 'inactivity' } - - it 'includes the reason directly' do - expect(mail.body.encoded).to include('Reason for deletion: inactivity') - end + describe '#account_deletion_user_initiated' do + it 'sets the correct subject for user-initiated deletion' do + mail = mailer.account_deletion_user_initiated(account, 'manual_deletion') + expect(mail.subject).to eq('Your Chatwoot account deletion has been scheduled') end end - describe 'contact_import_complete' do - let!(:data_import) { build(:data_import, total_records: 10, processed_records: 8) } - let(:mail) { described_class.with(account: account).contact_import_complete(data_import).deliver_now } - - it 'renders the subject' do - expect(mail.subject).to eq('Contact Import Completed') - end - - it 'renders the processed records' do - expect(mail.body.encoded).to include('Number of records imported: 8') - expect(mail.body.encoded).to include('Number of records failed: 2') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([admin.email]) + describe '#account_deletion_for_inactivity' do + it 'sets the correct subject for system-initiated deletion' do + mail = mailer.account_deletion_for_inactivity(account, 'Account Inactive') + expect(mail.subject).to eq('Your Chatwoot account is scheduled for deletion due to inactivity') end end - describe 'contact_import_failed' do - let(:mail) { described_class.with(account: account).contact_import_failed.deliver_now } - - it 'renders the subject' do - expect(mail.subject).to eq('Contact Import Failed') + describe '#format_deletion_date' do + it 'formats a valid date string' do + date_str = '2024-12-31T23:59:59Z' + formatted = described_class.new.send(:format_deletion_date, date_str) + expect(formatted).to eq('December 31, 2024') end - it 'renders the receiver email' do - expect(mail.to).to eq([admin.email]) - end - end - - describe 'contact_export_complete' do - let!(:file_url) { 'http://test.com/test' } - let(:mail) { described_class.with(account: account).contact_export_complete(file_url, admin.email).deliver_now } - - it 'renders the subject' do - expect(mail.subject).to eq("Your contact's export file is available to download.") + it 'handles blank dates' do + formatted = described_class.new.send(:format_deletion_date, nil) + expect(formatted).to eq('Unknown') end - it 'renders the receiver email' do - expect(mail.to).to eq([admin.email]) - end - end - - describe 'automation_rule_disabled' do - let(:rule) { instance_double(AutomationRule, name: 'Test Rule') } - let(:mail) { described_class.with(account: account).automation_rule_disabled(rule).deliver_now } - - it 'renders the subject' do - expect(mail.subject).to eq('Automation rule disabled due to validation errors.') - end - - it 'renders the receiver email' do - expect(mail.to).to eq([admin.email]) - end - - it 'includes the rule name in the email body' do - expect(mail.body.encoded).to include('Test Rule') + it 'handles invalid dates' do + formatted = described_class.new.send(:format_deletion_date, 'invalid-date') + expect(formatted).to eq('Unknown') end end end From f9c258f1a04b9ef56bfe6c30656054738c454bec Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Mon, 1 Sep 2025 13:02:35 +0530 Subject: [PATCH 02/94] fix: Prevent `[object Object]` when copying custom attributes (#12323) # Pull Request Template ## Description This PR fixes custom conversation attributes copying as `[object Object]` by enhancing the clipboard helper to properly serialize objects and handle different data types. Fixes [CW-5428](https://linear.app/chatwoot/issue/CW-5428/copying-custom-conversation-attribute-returns-object-object-instead-of), https://github.com/chatwoot/chatwoot/issues/12202 ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? ### Loom video https://www.loom.com/share/f52db17d4d524b3cbb5badb2b6f381eb?sid=2b34f38f-e95d-4981-be5f-6cb42a0212b9 ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --- app/javascript/shared/helpers/clipboard.js | 9 +- .../shared/helpers/specs/clipboard.spec.js | 174 ++++++++++++++++++ 2 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 app/javascript/shared/helpers/specs/clipboard.spec.js diff --git a/app/javascript/shared/helpers/clipboard.js b/app/javascript/shared/helpers/clipboard.js index dbadd4037..dcd25afae 100644 --- a/app/javascript/shared/helpers/clipboard.js +++ b/app/javascript/shared/helpers/clipboard.js @@ -2,11 +2,16 @@ * Writes a text string to the system clipboard. * * @async - * @param {string} text text to be written to the clipboard + * @param {string} data text to be written to the clipboard * @throws {Error} unable to copy text to clipboard */ -export const copyTextToClipboard = async text => { +export const copyTextToClipboard = async data => { try { + const text = + typeof data === 'object' && data !== null + ? JSON.stringify(data, null, 2) + : String(data ?? ''); + await navigator.clipboard.writeText(text); } catch (error) { throw new Error(`Unable to copy text to clipboard: ${error.message}`); diff --git a/app/javascript/shared/helpers/specs/clipboard.spec.js b/app/javascript/shared/helpers/specs/clipboard.spec.js new file mode 100644 index 000000000..c675edd35 --- /dev/null +++ b/app/javascript/shared/helpers/specs/clipboard.spec.js @@ -0,0 +1,174 @@ +import { copyTextToClipboard } from '../clipboard'; + +const mockWriteText = vi.fn(); +Object.assign(navigator, { + clipboard: { + writeText: mockWriteText, + }, +}); + +describe('copyTextToClipboard', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('with string input', () => { + it('copies plain text string to clipboard', async () => { + const text = 'Hello World'; + await copyTextToClipboard(text); + + expect(mockWriteText).toHaveBeenCalledWith('Hello World'); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + + it('copies empty string to clipboard', async () => { + const text = ''; + await copyTextToClipboard(text); + + expect(mockWriteText).toHaveBeenCalledWith(''); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + }); + + describe('with number input', () => { + it('converts number to string', async () => { + await copyTextToClipboard(42); + + expect(mockWriteText).toHaveBeenCalledWith('42'); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + + it('converts zero to string', async () => { + await copyTextToClipboard(0); + + expect(mockWriteText).toHaveBeenCalledWith('0'); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + }); + + describe('with boolean input', () => { + it('converts true to string', async () => { + await copyTextToClipboard(true); + + expect(mockWriteText).toHaveBeenCalledWith('true'); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + + it('converts false to string', async () => { + await copyTextToClipboard(false); + + expect(mockWriteText).toHaveBeenCalledWith('false'); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + }); + + describe('with null/undefined input', () => { + it('converts null to empty string', async () => { + await copyTextToClipboard(null); + + expect(mockWriteText).toHaveBeenCalledWith(''); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + + it('converts undefined to empty string', async () => { + await copyTextToClipboard(undefined); + + expect(mockWriteText).toHaveBeenCalledWith(''); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + }); + + describe('with object input', () => { + it('stringifies simple object with proper formatting', async () => { + const obj = { name: 'John', age: 30 }; + await copyTextToClipboard(obj); + + const expectedJson = JSON.stringify(obj, null, 2); + expect(mockWriteText).toHaveBeenCalledWith(expectedJson); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + + it('stringifies nested object with proper formatting', async () => { + const nestedObj = { + severity: { + user_id: 1181505, + user_name: 'test', + server_name: '[1253]test1253', + }, + }; + await copyTextToClipboard(nestedObj); + + const expectedJson = JSON.stringify(nestedObj, null, 2); + expect(mockWriteText).toHaveBeenCalledWith(expectedJson); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + + it('stringifies array with proper formatting', async () => { + const arr = [1, 2, { name: 'test' }]; + await copyTextToClipboard(arr); + + const expectedJson = JSON.stringify(arr, null, 2); + expect(mockWriteText).toHaveBeenCalledWith(expectedJson); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + + it('stringifies empty object', async () => { + const obj = {}; + await copyTextToClipboard(obj); + + expect(mockWriteText).toHaveBeenCalledWith('{}'); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + + it('stringifies empty array', async () => { + const arr = []; + await copyTextToClipboard(arr); + + expect(mockWriteText).toHaveBeenCalledWith('[]'); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + }); + + describe('error handling', () => { + it('throws error when clipboard API fails', async () => { + const error = new Error('Clipboard access denied'); + mockWriteText.mockRejectedValueOnce(error); + + await expect(copyTextToClipboard('test')).rejects.toThrow( + 'Unable to copy text to clipboard: Clipboard access denied' + ); + }); + + it('handles clipboard API not available', async () => { + // Temporarily remove clipboard API + const originalClipboard = navigator.clipboard; + delete navigator.clipboard; + + await expect(copyTextToClipboard('test')).rejects.toThrow( + 'Unable to copy text to clipboard:' + ); + + // Restore clipboard API + navigator.clipboard = originalClipboard; + }); + }); + + describe('edge cases', () => { + it('handles Date objects', async () => { + const date = new Date('2023-01-01T00:00:00.000Z'); + await copyTextToClipboard(date); + + const expectedJson = JSON.stringify(date, null, 2); + expect(mockWriteText).toHaveBeenCalledWith(expectedJson); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + + it('handles functions by converting to string', async () => { + const func = () => 'test'; + await copyTextToClipboard(func); + + expect(mockWriteText).toHaveBeenCalledWith(func.toString()); + expect(mockWriteText).toHaveBeenCalledTimes(1); + }); + }); +}); From c53e750be0df7176ce660b89dbd8c4b7eced7d36 Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Mon, 1 Sep 2025 13:22:55 +0530 Subject: [PATCH 03/94] feat: Display banner and handoff for bot-managed chats (#12292) --- .../widgets/conversation/ReplyBox.vue | 71 +--------- .../widgets/conversation/ReplyBoxBanner.vue | 129 ++++++++++++++++++ .../i18n/locale/en/conversation.json | 5 + 3 files changed, 137 insertions(+), 68 deletions(-) create mode 100644 app/javascript/dashboard/components/widgets/conversation/ReplyBoxBanner.vue diff --git a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue index cedf502a6..3db4779b0 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue @@ -15,7 +15,7 @@ import ReplyEmailHead from './ReplyEmailHead.vue'; import ReplyBottomPanel from 'dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue'; import ArticleSearchPopover from 'dashboard/routes/dashboard/helpcenter/components/ArticleSearch/SearchPopover.vue'; import MessageSignatureMissingAlert from './MessageSignatureMissingAlert.vue'; -import Banner from 'dashboard/components/ui/Banner.vue'; +import ReplyBoxBanner from './ReplyBoxBanner.vue'; import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants'; import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue'; import AudioRecorder from 'dashboard/components/widgets/WootWriter/AudioRecorder.vue'; @@ -53,8 +53,8 @@ export default { ArticleSearchPopover, AttachmentPreview, AudioRecorder, - Banner, CannedResponse, + ReplyBoxBanner, EmojiInput, MessageSignatureMissingAlert, ReplyBottomPanel, @@ -158,35 +158,6 @@ export default { return false; }, - assignedAgent: { - get() { - return this.currentChat.meta.assignee; - }, - set(agent) { - const agentId = agent ? agent.id : 0; - this.$store.dispatch('setCurrentChatAssignee', agent); - this.$store - .dispatch('assignAgent', { - conversationId: this.currentChat.id, - agentId, - }) - .then(() => { - useAlert(this.$t('CONVERSATION.CHANGE_AGENT')); - }); - }, - }, - showSelfAssignBanner() { - if (this.message !== '' && !this.isOnPrivateNote) { - if (!this.assignedAgent) { - return true; - } - if (this.assignedAgent.id !== this.currentUser.id) { - return true; - } - } - - return false; - }, showWhatsappTemplates() { return this.isAWhatsAppCloudChannel && !this.isPrivate; }, @@ -671,29 +642,6 @@ export default { hideContentTemplatesModal() { this.showContentTemplatesModal = false; }, - onClickSelfAssign() { - const { - account_id, - availability_status, - available_name, - email, - id, - name, - role, - avatar_url, - } = this.currentUser; - const selfAssign = { - account_id, - availability_status, - available_name, - email, - id, - name, - role, - thumbnail: avatar_url, - }; - this.assignedAgent = selfAssign; - }, confirmOnSendReply() { if (this.isReplyButtonDisabled) { return; @@ -1118,16 +1066,7 @@ export default {