mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-30 02:32:29 +00:00
chore: Update account deletion email copy (#12317)
Update the email copies for account deletion emails ## Manual Deletion <img width="1378" height="678" alt="Screenshot 2025-08-29 at 2 41 40 PM" src="https://github.com/user-attachments/assets/63d7ad97-ad51-4a8b-9ef3-d427fa467f8a" /> ## Inactive Deletion <img width="2946" height="1808" alt="image" src="https://github.com/user-attachments/assets/bb50d08c-8701-4f93-af29-0b1d948a4009" /> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Sojan Jose <sojan@pepalo.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<p>Hello,</p>
|
||||
|
||||
<p>Your account <strong>{{ meta.account_name }}</strong> has been marked for deletion. The account will be permanently deleted on <strong>{{ meta.deletion_date }}</strong>.</p>
|
||||
|
||||
{% if meta.reason == 'manual_deletion' %}
|
||||
<p>This action was requested by one of the administrators of your account.</p>
|
||||
{% else %}
|
||||
<p>Reason for deletion: {{ meta.reason }}</p>
|
||||
{% endif %}
|
||||
|
||||
<p>If this was done in error, you can cancel the deletion process by visiting your account settings.</p>
|
||||
|
||||
<p><a href="{{ action_url }}">Cancel Account Deletion</a></p>
|
||||
|
||||
<p>Thank you,<br>
|
||||
Team Chatwoot</p>
|
||||
@@ -0,0 +1,21 @@
|
||||
<p>Hello there,</p>
|
||||
|
||||
<p>We've noticed that your Chatwoot account <strong>{{ meta.account_name }}</strong> has been inactive for some time. Because of this, it's scheduled for deletion on <strong>{{ meta.deletion_date }}</strong>.</p>
|
||||
|
||||
<p><strong>How do I keep my account?</strong></p>
|
||||
|
||||
<p>Log in to your Chatwoot account before <strong>{{ meta.deletion_date }}</strong>. From your account settings, you can <a href="{{ action_url }}">cancel the deletion</a> and continue using your account.</p>
|
||||
|
||||
<p><strong>What happens if I don't cancel?</strong></p>
|
||||
|
||||
<p>Unless you cancel the account deletion before <strong>{{ meta.deletion_date }}</strong>, your account and all associated data — including conversations, contacts, reports, and settings — will be permanently deleted.</p>
|
||||
|
||||
<p><strong>Why are we doing this?</strong></p>
|
||||
|
||||
<p>To keep things secure and efficient, we regularly remove inactive accounts so our systems remain optimized for active teams.</p>
|
||||
|
||||
<p>If you have any questions, feel free to reach us at <a href="mailto:hello@chatwoot.com">hello@chatwoot.com</a>.</p>
|
||||
|
||||
<p>— The Chatwoot Team</p>
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<p>Hello there,</p>
|
||||
|
||||
<p>An account administrator has requested deletion of the Chatwoot account <strong>{{ meta.account_name }}</strong>. The account is scheduled for deletion on <strong>{{ meta.deletion_date }}</strong>.</p>
|
||||
|
||||
<p><strong>What happens next?</strong></p>
|
||||
|
||||
<ul>
|
||||
<li>The account will remain accessible until the scheduled deletion date.</li>
|
||||
<li>After that, all data including conversations, contacts, integrations, and settings will be permanently removed.</li>
|
||||
</ul>
|
||||
|
||||
<p>If you change your mind before the deletion date, you can <a href="{{ action_url }}">cancel this request</a> by visiting your account settings.</p>
|
||||
|
||||
<p>— The Chatwoot Team</p>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user