diff --git a/Gemfile b/Gemfile
index 38dee9bc4..bee9eddc1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -71,7 +71,7 @@ gem 'barnes'
##--- gems for authentication & authorization ---##
gem 'devise', '>= 4.9.4'
gem 'devise-secure_password', git: 'https://github.com/chatwoot/devise-secure_password', branch: 'chatwoot'
-gem 'devise_token_auth'
+gem 'devise_token_auth', '>= 1.2.3'
# authorization
gem 'jwt'
gem 'pundit'
@@ -166,7 +166,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-rails_csrf_protection', '~> 1.0'
+gem 'omniauth-rails_csrf_protection', '~> 1.0', '>= 1.0.2'
## Gems for reponse bot
# adds cosine similarity to postgres using vector extension
diff --git a/Gemfile.lock b/Gemfile.lock
index 5dc67bef1..2f890deaa 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -148,7 +148,7 @@ GEM
barnes (0.0.9)
multi_json (~> 1)
statsd-ruby (~> 1.1)
- base64 (0.1.1)
+ base64 (0.2.0)
bcrypt (3.1.20)
bigdecimal (3.1.7)
bindex (0.8.1)
@@ -200,10 +200,10 @@ GEM
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
- devise_token_auth (1.2.1)
+ devise_token_auth (1.2.3)
bcrypt (~> 3.0)
devise (> 3.5.2, < 5)
- rails (>= 4.2.0, < 7.1)
+ rails (>= 4.2.0, < 7.2)
diff-lcs (1.5.0)
digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.0)
@@ -369,7 +369,7 @@ GEM
mini_mime (>= 1.0.0)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
- i18n (1.14.4)
+ i18n (1.14.5)
concurrent-ruby (~> 1.0)
image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5)
@@ -474,7 +474,7 @@ GEM
uri
net-http-persistent (4.0.2)
connection_pool (~> 2.2)
- net-imap (0.4.10)
+ net-imap (0.4.11)
date
net-protocol
net-pop (0.1.2)
@@ -489,15 +489,15 @@ GEM
sidekiq
newrelic_rpm (9.6.0)
base64
- nio4r (2.7.1)
- nokogiri (1.16.4)
+ nio4r (2.7.3)
+ nokogiri (1.16.5)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
- nokogiri (1.16.4-arm64-darwin)
+ nokogiri (1.16.5-arm64-darwin)
racc (~> 1.4)
- nokogiri (1.16.4-x86_64-darwin)
+ nokogiri (1.16.5-x86_64-darwin)
racc (~> 1.4)
- nokogiri (1.16.4-x86_64-linux)
+ nokogiri (1.16.5-x86_64-linux)
racc (~> 1.4)
oauth (1.1.0)
oauth-tty (~> 1.0, >= 1.0.1)
@@ -524,7 +524,7 @@ GEM
omniauth-oauth2 (1.8.0)
oauth2 (>= 1.4, < 3)
omniauth (~> 2.0)
- omniauth-rails_csrf_protection (1.0.1)
+ omniauth-rails_csrf_protection (1.0.2)
actionpack (>= 4.2)
omniauth (~> 2.0)
openssl (3.1.0)
@@ -819,7 +819,7 @@ GEM
working_hours (1.4.1)
activesupport (>= 3.2)
tzinfo
- zeitwerk (2.6.13)
+ zeitwerk (2.6.14)
PLATFORMS
arm64-darwin-20
@@ -860,7 +860,7 @@ DEPENDENCIES
debug (~> 1.8)
devise (>= 4.9.4)
devise-secure_password!
- devise_token_auth
+ devise_token_auth (>= 1.2.3)
dotenv-rails
down
elastic-apm
@@ -904,7 +904,7 @@ DEPENDENCIES
omniauth (>= 2.1.2)
omniauth-google-oauth2 (>= 1.1.2)
omniauth-oauth2
- omniauth-rails_csrf_protection (~> 1.0)
+ omniauth-rails_csrf_protection (~> 1.0, >= 1.0.2)
pg
pg_search
pgvector
diff --git a/app.json b/app.json
index 4ee025f06..d50040814 100644
--- a/app.json
+++ b/app.json
@@ -55,7 +55,7 @@
"plan": "heroku-redis:mini"
},
{
- "plan": "heroku-postgresql:mini"
+ "plan": "heroku-postgresql:essential-0"
}
],
"stack": "heroku-20",
diff --git a/app/builders/contact_inbox_with_contact_builder.rb b/app/builders/contact_inbox_with_contact_builder.rb
index 6e913eda8..2f30093db 100644
--- a/app/builders/contact_inbox_with_contact_builder.rb
+++ b/app/builders/contact_inbox_with_contact_builder.rb
@@ -19,9 +19,9 @@ class ContactInboxWithContactBuilder
ActiveRecord::Base.transaction(requires_new: true) do
build_contact_with_contact_inbox
- update_contact_avatar(@contact) unless @contact.avatar.attached?
- @contact_inbox
end
+ update_contact_avatar(@contact) unless @contact.avatar.attached?
+ @contact_inbox
end
private
diff --git a/app/controllers/api/v1/accounts/bulk_actions_controller.rb b/app/controllers/api/v1/accounts/bulk_actions_controller.rb
index 832f5a49b..34db47861 100644
--- a/app/controllers/api/v1/accounts/bulk_actions_controller.rb
+++ b/app/controllers/api/v1/accounts/bulk_actions_controller.rb
@@ -21,6 +21,6 @@ class Api::V1::Accounts::BulkActionsController < Api::V1::Accounts::BaseControll
end
def permitted_params
- params.permit(:type, ids: [], fields: [:status, :assignee_id, :team_id], labels: [add: [], remove: []])
+ params.permit(:type, :snoozed_until, ids: [], fields: [:status, :assignee_id, :team_id], labels: [add: [], remove: []])
end
end
diff --git a/app/controllers/api/v1/accounts/contacts_controller.rb b/app/controllers/api/v1/accounts/contacts_controller.rb
index 729db34b5..250c64a86 100644
--- a/app/controllers/api/v1/accounts/contacts_controller.rb
+++ b/app/controllers/api/v1/accounts/contacts_controller.rb
@@ -46,7 +46,8 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
def export
column_names = params['column_names']
- Account::ContactsExportJob.perform_later(Current.account.id, column_names, Current.user.email)
+ filter_params = { :payload => params.permit!['payload'], :label => params.permit!['label'] }
+ Account::ContactsExportJob.perform_later(Current.account.id, Current.user.id, column_names, filter_params)
head :ok, message: I18n.t('errors.contacts.export.success')
end
@@ -61,7 +62,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
def show; end
def filter
- result = ::Contacts::FilterService.new(params.permit!, current_user).perform
+ result = ::Contacts::FilterService.new(Current.account, Current.user, params.permit!).perform
contacts = result[:contacts]
@contacts_count = result[:count]
@contacts = fetch_contacts(contacts)
diff --git a/app/javascript/dashboard/api/contacts.js b/app/javascript/dashboard/api/contacts.js
index 61312dd24..85c4bba1d 100644
--- a/app/javascript/dashboard/api/contacts.js
+++ b/app/javascript/dashboard/api/contacts.js
@@ -77,8 +77,8 @@ class ContactAPI extends ApiClient {
return axios.delete(`${this.url}/${contactId}/avatar`);
}
- exportContacts() {
- return axios.get(`${this.url}/export`);
+ exportContacts(queryPayload) {
+ return axios.post(`${this.url}/export`, queryPayload);
}
}
diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue
index e7fae6e01..348d8572a 100644
--- a/app/javascript/dashboard/components/ChatList.vue
+++ b/app/javascript/dashboard/components/ChatList.vue
@@ -288,7 +288,6 @@ export default {
foldersQuery: {},
showAddFoldersModal: false,
showDeleteFoldersModal: false,
- selectedConversations: [],
selectedInboxes: [],
isContextMenuOpen: false,
appliedFilter: [],
@@ -329,6 +328,7 @@ export default {
inboxesList: 'inboxes/getInboxes',
campaigns: 'campaigns/getAllCampaigns',
labels: 'labels/getLabels',
+ selectedConversations: 'bulkActions/getSelectedConversationIds',
}),
hasAppliedFilters() {
return this.appliedFilters.length !== 0;
@@ -799,7 +799,7 @@ export default {
});
},
resetBulkActions() {
- this.selectedConversations = [];
+ this.$store.dispatch('bulkActions/clearSelectedConversationIds');
this.selectedInboxes = [];
},
onBasicFilterChange(value, type) {
@@ -830,12 +830,16 @@ export default {
return this.selectedConversations.includes(id);
},
selectConversation(conversationId, inboxId) {
- this.selectedConversations.push(conversationId);
+ this.$store.dispatch(
+ 'bulkActions/setSelectedConversationIds',
+ conversationId
+ );
this.selectedInboxes.push(inboxId);
},
deSelectConversation(conversationId, inboxId) {
- this.selectedConversations = this.selectedConversations.filter(
- item => item !== conversationId
+ this.$store.dispatch(
+ 'bulkActions/removeSelectedConversationIds',
+ conversationId
);
this.selectedInboxes = this.selectedInboxes.filter(
item => item !== inboxId
@@ -843,7 +847,10 @@ export default {
},
selectAllConversations(check) {
if (check) {
- this.selectedConversations = this.conversationList.map(item => item.id);
+ this.$store.dispatch(
+ 'bulkActions/setSelectedConversationIds',
+ this.conversationList.map(item => item.id)
+ );
this.selectedInboxes = this.conversationList.map(item => item.inbox_id);
} else {
this.resetBulkActions();
@@ -859,7 +866,7 @@ export default {
assignee_id: agent.id,
},
});
- this.selectedConversations = [];
+ this.$store.dispatch('bulkActions/clearSelectedConversationIds');
if (conversationId) {
this.showAlert(
this.$t(
@@ -957,7 +964,7 @@ export default {
add: labels,
},
});
- this.selectedConversations = [];
+ this.$store.dispatch('bulkActions/clearSelectedConversationIds');
if (conversationId) {
this.showAlert(
this.$t(
@@ -984,13 +991,13 @@ export default {
team_id: team.id,
},
});
- this.selectedConversations = [];
+ this.$store.dispatch('bulkActions/clearSelectedConversationIds');
this.showAlert(this.$t('BULK_ACTION.TEAMS.ASSIGN_SUCCESFUL'));
} catch (err) {
this.showAlert(this.$t('BULK_ACTION.TEAMS.ASSIGN_FAILED'));
}
},
- async onUpdateConversations(status) {
+ async onUpdateConversations(status, snoozedUntil) {
try {
await this.$store.dispatch('bulkActions/process', {
type: 'Conversation',
@@ -998,8 +1005,9 @@ export default {
fields: {
status,
},
+ snoozed_until: snoozedUntil,
});
- this.selectedConversations = [];
+ this.$store.dispatch('bulkActions/clearSelectedConversationIds');
this.showAlert(this.$t('BULK_ACTION.UPDATE.UPDATE_SUCCESFUL'));
} catch (err) {
this.showAlert(this.$t('BULK_ACTION.UPDATE.UPDATE_FAILED'));
diff --git a/app/javascript/dashboard/components/widgets/WootWriter/AudioRecorder.vue b/app/javascript/dashboard/components/widgets/WootWriter/AudioRecorder.vue
index 3722ea208..aa566fd4d 100644
--- a/app/javascript/dashboard/components/widgets/WootWriter/AudioRecorder.vue
+++ b/app/javascript/dashboard/components/widgets/WootWriter/AudioRecorder.vue
@@ -5,6 +5,7 @@
+
-