feat: Contact Exports (#7258)

This commit is contained in:
Tejaswini Chile
2023-06-13 09:18:43 +05:30
committed by GitHub
parent 429ec7194f
commit 23ca6d56f9
16 changed files with 240 additions and 0 deletions
@@ -42,6 +42,12 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
head :ok
end
def export
column_names = params['column_names']
Account::ContactsExportJob.perform_later(Current.account.id, column_names)
head :ok, message: I18n.t('errors.contacts.export.success')
end
# returns online contacts
def active
contacts = Current.account.contacts.where(id: ::OnlineStatusTracker
+4
View File
@@ -75,6 +75,10 @@ class ContactAPI extends ApiClient {
destroyAvatar(contactId) {
return axios.delete(`${this.url}/${contactId}/avatar`);
}
exportContacts() {
return axios.get(`${this.url}/export`);
}
}
export default new ContactAPI();
@@ -73,6 +73,13 @@
"SUCCESS_MESSAGE": "Contacts saved successfully",
"ERROR_MESSAGE": "There was an error, please try again"
},
"EXPORT_CONTACTS": {
"BUTTON_LABEL": "Export",
"TITLE": "Export Contacts",
"DESC": "Export contacts to a CSV file.",
"SUCCESS_MESSAGE": "Export is in progress, You will be notified via email when export file is ready to dowanlod.",
"ERROR_MESSAGE": "There was an error, please try again"
},
"DELETE_NOTE": {
"CONFIRM": {
"TITLE": "Confirm Deletion",
@@ -5,6 +5,7 @@
:search-query="searchQuery"
:segments-id="segmentsId"
:on-search-submit="onSearchSubmit"
:on-export-submit="onExportSubmit"
this-selected-contact-id=""
:on-input-search="onInputSearch"
:on-toggle-create="onToggleCreate"
@@ -92,6 +93,7 @@ import filterQueryGenerator from '../../../../helper/filterQueryGenerator';
import AddCustomViews from 'dashboard/routes/dashboard/customviews/AddCustomViews';
import DeleteCustomViews from 'dashboard/routes/dashboard/customviews/DeleteCustomViews';
import { CONTACTS_EVENTS } from '../../../../helper/AnalyticsHelper/events';
import alertMixin from 'shared/mixins/alertMixin';
import countries from 'shared/constants/countries.js';
import { generateValuesForEditCustomViews } from 'dashboard/helper/customViewsHelper';
@@ -110,6 +112,7 @@ export default {
AddCustomViews,
DeleteCustomViews,
},
mixins: [alertMixin],
props: {
label: { type: String, default: '' },
segmentsId: {
@@ -386,6 +389,16 @@ export default {
this.$store.dispatch('contacts/clearContactFilters');
this.fetchContacts(this.pageParameter);
},
onExportSubmit() {
try {
this.$store.dispatch('contacts/export');
this.showAlert(this.$t('EXPORT_CONTACTS.SUCCESS_MESSAGE'));
} catch (error) {
this.showAlert(
error.message || this.$t('EXPORT_CONTACTS.ERROR_MESSAGE')
);
}
},
setParamsForEditSegmentModal() {
// Here we are setting the params for edit segment modal to show the existing values.
@@ -87,6 +87,16 @@
>
{{ $t('IMPORT_CONTACTS.BUTTON_LABEL') }}
</woot-button>
<woot-button
v-if="isAdmin"
color-scheme="info"
icon="upload"
class="clear"
@click="onExportSubmit"
>
{{ $t('EXPORT_CONTACTS.BUTTON_LABEL') }}
</woot-button>
</div>
</div>
</header>
@@ -127,6 +137,10 @@ export default {
type: Function,
default: () => {},
},
onExportSubmit: {
type: Function,
default: () => {},
},
onToggleFilter: {
type: Function,
default: () => {},
@@ -140,6 +140,20 @@ export const actions = {
}
},
export: async ({ commit }) => {
try {
await ContactAPI.exportContacts();
commit(types.SET_CONTACT_UI_FLAG, { isCreating: false });
} catch (error) {
commit(types.SET_CONTACT_UI_FLAG, { isCreating: false });
if (error.response?.data?.message) {
throw new Error(error.response.data.message);
} else {
throw new Error(error);
}
}
},
delete: async ({ commit }, id) => {
commit(types.SET_CONTACT_UI_FLAG, { isDeleting: true });
try {
+47
View File
@@ -0,0 +1,47 @@
class Account::ContactsExportJob < ApplicationJob
queue_as :low
def perform(account_id, column_names)
account = Account.find(account_id)
headers = valid_headers(column_names)
generate_csv(account, headers)
file_url = account_contact_export_url(account)
AdministratorNotifications::ChannelNotificationsMailer.with(account: account).contact_export_complete(file_url)&.deliver_later
end
def generate_csv(account, headers)
csv_data = CSV.generate do |csv|
csv << headers
account.contacts.each do |contact|
csv << headers.map { |header| contact.send(header) }
end
end
attach_export_file(account, csv_data)
end
def valid_headers(column_names)
columns = (column_names.presence || default_columns)
headers = columns.map { |column| column if Contact.column_names.include?(column) }
headers.compact
end
def attach_export_file(account, csv_data)
return if csv_data.blank?
account.contacts_export.attach(
io: StringIO.new(csv_data),
filename: "#{account.name}_#{account.id}_contacts.csv",
content_type: 'text/csv'
)
end
def account_contact_export_url(account)
Rails.application.routes.url_helpers.rails_blob_url(account.contacts_export)
end
def default_columns
%w[id name email phone_number]
end
end
@@ -43,6 +43,14 @@ class AdministratorNotifications::ChannelNotificationsMailer < ApplicationMailer
send_mail_with_liquid(to: admin_emails, subject: subject) and return
end
def contact_export_complete(file_url)
return unless smtp_config_set_or_development?
@action_url = file_url
subject = "Your contact's export file is available to download."
send_mail_with_liquid(to: admin_emails, subject: subject) and return
end
private
def admin_emails
+2
View File
@@ -77,6 +77,8 @@ class Account < ApplicationRecord
has_many :whatsapp_channels, dependent: :destroy_async, class_name: '::Channel::Whatsapp'
has_many :working_hours, dependent: :destroy_async
has_one_attached :contacts_export
enum locale: LANGUAGES_CONFIG.map { |key, val| [val[:iso_639_1_code], key] }.to_h
enum status: { active: 0, suspended: 1 }
+4
View File
@@ -11,6 +11,10 @@ class ContactPolicy < ApplicationPolicy
@account_user.administrator?
end
def export?
@account_user.administrator?
end
def search?
true
end
@@ -0,0 +1,8 @@
<p>Hi</p>
<p>Your contact export file is ready to download.</p>
<p>
Click <a href="{{ action_url }}">here</a> to download the export file.
</p>