Merge branch 'develop' into fix/next-message-bubbles
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -479,7 +479,7 @@ provideMessageContext({
|
||||
>
|
||||
<div
|
||||
v-if="!shouldGroupWithNext && shouldShowAvatar"
|
||||
v-tooltip.left-end="avatarTooltip"
|
||||
v-tooltip.right-end="avatarTooltip"
|
||||
class="[grid-area:avatar] flex items-end"
|
||||
>
|
||||
<Avatar v-bind="avatarInfo" :size="20" />
|
||||
|
||||
@@ -31,7 +31,7 @@ const senderName = computed(() => {
|
||||
<template>
|
||||
<BaseBubble class="overflow-hidden p-3" data-bubble-name="attachment">
|
||||
<div class="grid gap-4 min-w-64">
|
||||
<div class="grid gap-3 z-20">
|
||||
<div class="grid gap-3">
|
||||
<div
|
||||
class="size-8 rounded-lg grid place-content-center"
|
||||
:class="iconBgColor"
|
||||
|
||||
@@ -34,7 +34,7 @@ const joinTheCall = async () => {
|
||||
};
|
||||
|
||||
const leaveTheRoom = () => {
|
||||
this.dyteAuthToken = '';
|
||||
dyteAuthToken.value = '';
|
||||
};
|
||||
const action = computed(() => ({
|
||||
label: t('INTEGRATION_SETTINGS.DYTE.CLICK_HERE_TO_JOIN'),
|
||||
@@ -60,7 +60,7 @@ const action = computed(() => ({
|
||||
allow="camera;microphone;fullscreen;display-capture;picture-in-picture;clipboard-write;"
|
||||
/>
|
||||
<button
|
||||
class="px-4 py-2 text-sm rounded-lg bg-n-solid-3"
|
||||
class="px-4 py-2 text-sm rounded-lg bg-n-solid-3 mt-3"
|
||||
@click="leaveTheRoom"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.DYTE.LEAVE_THE_ROOM') }}
|
||||
|
||||
@@ -266,7 +266,7 @@
|
||||
"ATTRIBUTE_WARNING": "Contact details of <strong>{primaryContactName}</strong> will be copied to <strong>{parentContactName}</strong>."
|
||||
},
|
||||
"SEARCH": {
|
||||
"ERROR": "ERROR_MESSAGE"
|
||||
"ERROR_MESSAGE": "Something went wrong. Please try again later."
|
||||
},
|
||||
"FORM": {
|
||||
"SUBMIT": " Merge contacts",
|
||||
|
||||
@@ -112,21 +112,32 @@ export default {
|
||||
};
|
||||
},
|
||||
getChartOptions(metric) {
|
||||
let tooltips = {};
|
||||
if (this.isAverageMetricType(metric.KEY)) {
|
||||
tooltips.callbacks = {
|
||||
label: tooltipItem => {
|
||||
return this.$t(metric.TOOLTIP_TEXT, {
|
||||
metricValue: formatTime(tooltipItem.yLabel),
|
||||
conversationCount:
|
||||
this.accountReport.data[metric.KEY][tooltipItem.index].count,
|
||||
});
|
||||
},
|
||||
};
|
||||
const baseOptions = METRIC_CHART[metric.KEY].scales;
|
||||
|
||||
// If not an average metric type, return base options early
|
||||
if (!this.isAverageMetricType(metric.KEY)) {
|
||||
return baseOptions;
|
||||
}
|
||||
|
||||
// Only create tooltip config for time-based metrics
|
||||
return {
|
||||
scales: METRIC_CHART[metric.KEY].scales,
|
||||
tooltips: tooltips,
|
||||
...baseOptions,
|
||||
plugins: {
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: ({ raw, dataIndex }) => {
|
||||
const value = raw || 0;
|
||||
const count =
|
||||
this.accountReport.data[metric.KEY][dataIndex]?.count || 0;
|
||||
|
||||
return this.$t(metric.TOOLTIP_TEXT, {
|
||||
metricValue: formatTime(value),
|
||||
conversationCount: count,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
+12
-6
@@ -82,14 +82,20 @@ class Inbox < ApplicationRecord
|
||||
|
||||
scope :order_by_name, -> { order('lower(name) ASC') }
|
||||
|
||||
def add_member(user_id)
|
||||
member = inbox_members.new(user_id: user_id)
|
||||
member.save!
|
||||
# Adds multiple members to the inbox
|
||||
# @param user_ids [Array<Integer>] Array of user IDs to add as members
|
||||
# @return [void]
|
||||
def add_members(user_ids)
|
||||
inbox_members.create!(user_ids.map { |user_id| { user_id: user_id } })
|
||||
update_account_cache
|
||||
end
|
||||
|
||||
def remove_member(user_id)
|
||||
member = inbox_members.find_by!(user_id: user_id)
|
||||
member.try(:destroy)
|
||||
# Removes multiple members from the inbox
|
||||
# @param user_ids [Array<Integer>] Array of user IDs to remove
|
||||
# @return [void]
|
||||
def remove_members(user_ids)
|
||||
inbox_members.where(user_id: user_ids).destroy_all
|
||||
update_account_cache
|
||||
end
|
||||
|
||||
def facebook?
|
||||
|
||||
+16
-4
@@ -31,12 +31,24 @@ class Team < ApplicationRecord
|
||||
self.name = name.downcase if attribute_present?('name')
|
||||
end
|
||||
|
||||
def add_member(user_id)
|
||||
team_members.find_or_create_by(user_id: user_id)&.user
|
||||
# Adds multiple members to the team
|
||||
# @param user_ids [Array<Integer>] Array of user IDs to add as members
|
||||
# @return [Array<User>] Array of newly added members
|
||||
def add_members(user_ids)
|
||||
team_members_to_create = user_ids.map { |user_id| { user_id: user_id } }
|
||||
created_members = team_members.create(team_members_to_create)
|
||||
added_users = created_members.filter_map(&:user)
|
||||
|
||||
update_account_cache
|
||||
added_users
|
||||
end
|
||||
|
||||
def remove_member(user_id)
|
||||
team_members.find_by(user_id: user_id)&.destroy!
|
||||
# Removes multiple members from the team
|
||||
# @param user_ids [Array<Integer>] Array of user IDs to remove
|
||||
# @return [void]
|
||||
def remove_members(user_ids)
|
||||
team_members.where(user_id: user_ids).destroy_all
|
||||
update_account_cache
|
||||
end
|
||||
|
||||
def messages
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
json.payload do
|
||||
json.array! @contactable_inboxes do |contactable_inbox|
|
||||
json.inbox do
|
||||
json.partial! 'api/v1/models/inbox', formats: [:json], resource: contactable_inbox[:inbox]
|
||||
json.partial! 'api/v1/models/inbox_slim', formats: [:json], resource: contactable_inbox[:inbox]
|
||||
end
|
||||
json.source_id contactable_inbox[:source_id]
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
json.source_id resource.source_id
|
||||
json.inbox do
|
||||
json.partial! 'api/v1/models/inbox', formats: [:json], resource: resource.inbox
|
||||
json.partial! 'api/v1/models/inbox_slim', formats: [:json], resource: resource.inbox
|
||||
end
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
json.id resource.id
|
||||
json.avatar_url resource.try(:avatar_url)
|
||||
json.channel_id resource.channel_id
|
||||
json.name resource.name
|
||||
json.channel_type resource.channel_type
|
||||
json.provider resource.channel.try(:provider)
|
||||
json.email resource.channel.try(:email) if resource.email?
|
||||
@@ -2,7 +2,7 @@ class Captain::Documents::CrawlJob < ApplicationJob
|
||||
queue_as :low
|
||||
|
||||
def perform(document)
|
||||
if InstallationConfig.find_by(name: 'CAPTAIN_FIRECRAWL_API_KEY').present?
|
||||
if InstallationConfig.find_by(name: 'CAPTAIN_FIRECRAWL_API_KEY')&.value.present?
|
||||
perform_firecrawl_crawl(document)
|
||||
else
|
||||
perform_simple_crawl(document)
|
||||
|
||||
@@ -67,6 +67,38 @@ RSpec.describe 'Contacts API', type: :request do
|
||||
expect(contact_inboxes).to eq([])
|
||||
end
|
||||
|
||||
it 'returns limited information on inboxes' do
|
||||
get "/api/v1/accounts/#{account.id}/contacts?include_contact_inboxes=true",
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
response_body = response.parsed_body
|
||||
|
||||
contact_emails = response_body['payload'].pluck('email')
|
||||
contact_inboxes = response_body['payload'].pluck('contact_inboxes').flatten.compact
|
||||
expect(contact_emails).to include(contact.email)
|
||||
first_inbox = contact_inboxes[0]['inbox']
|
||||
expect(first_inbox).to be_a(Hash)
|
||||
expect(first_inbox).to include('id', 'channel_id', 'channel_type', 'name', 'avatar_url', 'provider')
|
||||
|
||||
expect(first_inbox).not_to include('imap_login',
|
||||
'imap_password',
|
||||
'imap_address',
|
||||
'imap_port',
|
||||
'imap_enabled',
|
||||
'imap_enable_ssl')
|
||||
|
||||
expect(first_inbox).not_to include('smtp_login',
|
||||
'smtp_password',
|
||||
'smtp_address',
|
||||
'smtp_port',
|
||||
'smtp_enabled',
|
||||
'smtp_domain')
|
||||
|
||||
expect(first_inbox).not_to include('hmac_token', 'provider_config')
|
||||
end
|
||||
|
||||
it 'returns all contacts with company name desc order' do
|
||||
get "/api/v1/accounts/#{account.id}/contacts?include_contact_inboxes=false&sort=-company",
|
||||
headers: admin.create_new_auth_token,
|
||||
|
||||
@@ -255,28 +255,30 @@ RSpec.describe 'Inbox Member API', type: :request do
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
|
||||
it 'renders error on invalid params' do
|
||||
it 'ignores invalid params' do
|
||||
params = { inbox_id: inbox.id, user_ids: ['invalid'] }
|
||||
original_count = inbox.inbox_members&.count
|
||||
|
||||
delete "/api/v1/accounts/#{account.id}/inbox_members",
|
||||
headers: administrator.create_new_auth_token,
|
||||
params: params,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
expect(response.body).to include('Resource could not be found')
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(inbox.inbox_members&.count).to eq(original_count)
|
||||
end
|
||||
|
||||
it 'renders error on non member params' do
|
||||
it 'ignores non member params' do
|
||||
params = { inbox_id: inbox.id, user_ids: [non_member_agent.id] }
|
||||
original_count = inbox.inbox_members&.count
|
||||
|
||||
delete "/api/v1/accounts/#{account.id}/inbox_members",
|
||||
headers: administrator.create_new_auth_token,
|
||||
params: params,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
expect(response.body).to include('Resource could not be found')
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(inbox.inbox_members&.count).to eq(original_count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+33
-12
@@ -41,29 +41,50 @@ RSpec.describe Inbox do
|
||||
it_behaves_like 'avatarable'
|
||||
end
|
||||
|
||||
describe '#add_member' do
|
||||
describe '#add_members' do
|
||||
let(:inbox) { FactoryBot.create(:inbox) }
|
||||
let(:user) { FactoryBot.create(:user) }
|
||||
|
||||
it do
|
||||
expect(inbox.inbox_members.size).to eq(0)
|
||||
before do
|
||||
allow(Rails.configuration.dispatcher).to receive(:dispatch)
|
||||
end
|
||||
|
||||
inbox.add_member(user.id)
|
||||
expect(inbox.reload.inbox_members.size).to eq(1)
|
||||
it 'handles adds all members and resets cache keys' do
|
||||
users = FactoryBot.create_list(:user, 3)
|
||||
inbox.add_members(users.map(&:id))
|
||||
expect(inbox.reload.inbox_members.size).to eq(3)
|
||||
|
||||
expect(Rails.configuration.dispatcher).to have_received(:dispatch).at_least(:once)
|
||||
.with(
|
||||
'account.cache_invalidated',
|
||||
kind_of(Time),
|
||||
account: inbox.account,
|
||||
cache_keys: inbox.account.cache_keys
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#remove_member' do
|
||||
describe '#remove_members' do
|
||||
let(:inbox) { FactoryBot.create(:inbox) }
|
||||
let(:user) { FactoryBot.create(:user) }
|
||||
let(:users) { FactoryBot.create_list(:user, 3) }
|
||||
|
||||
before { inbox.add_member(user.id) }
|
||||
before do
|
||||
inbox.add_members(users.map(&:id))
|
||||
allow(Rails.configuration.dispatcher).to receive(:dispatch)
|
||||
end
|
||||
|
||||
it do
|
||||
expect(inbox.inbox_members.size).to eq(1)
|
||||
it 'removes the members and resets cache keys' do
|
||||
expect(inbox.reload.inbox_members.size).to eq(3)
|
||||
|
||||
inbox.remove_member(user.id)
|
||||
inbox.remove_members(users.map(&:id))
|
||||
expect(inbox.reload.inbox_members.size).to eq(0)
|
||||
|
||||
expect(Rails.configuration.dispatcher).to have_received(:dispatch).at_least(:once)
|
||||
.with(
|
||||
'account.cache_invalidated',
|
||||
kind_of(Time),
|
||||
account: inbox.account,
|
||||
cache_keys: inbox.account.cache_keys
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -6,4 +6,51 @@ RSpec.describe Team do
|
||||
it { is_expected.to have_many(:conversations) }
|
||||
it { is_expected.to have_many(:team_members) }
|
||||
end
|
||||
|
||||
describe '#add_members' do
|
||||
let(:team) { FactoryBot.create(:team) }
|
||||
|
||||
before do
|
||||
allow(Rails.configuration.dispatcher).to receive(:dispatch)
|
||||
end
|
||||
|
||||
it 'handles adds all members and resets cache keys' do
|
||||
users = FactoryBot.create_list(:user, 3)
|
||||
team.add_members(users.map(&:id))
|
||||
expect(team.reload.team_members.size).to eq(3)
|
||||
|
||||
expect(Rails.configuration.dispatcher).to have_received(:dispatch).at_least(:once)
|
||||
.with(
|
||||
'account.cache_invalidated',
|
||||
kind_of(Time),
|
||||
account: team.account,
|
||||
cache_keys: team.account.cache_keys
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#remove_members' do
|
||||
let(:team) { FactoryBot.create(:team) }
|
||||
let(:users) { FactoryBot.create_list(:user, 3) }
|
||||
|
||||
before do
|
||||
team.add_members(users.map(&:id))
|
||||
allow(Rails.configuration.dispatcher).to receive(:dispatch)
|
||||
end
|
||||
|
||||
it 'removes the members and resets cache keys' do
|
||||
expect(team.reload.team_members.size).to eq(3)
|
||||
|
||||
team.remove_members(users.map(&:id))
|
||||
expect(team.reload.team_members.size).to eq(0)
|
||||
|
||||
expect(Rails.configuration.dispatcher).to have_received(:dispatch).at_least(:once)
|
||||
.with(
|
||||
'account.cache_invalidated',
|
||||
kind_of(Time),
|
||||
account: team.account,
|
||||
cache_keys: team.account.cache_keys
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -55,10 +55,6 @@ const tailwindConfig = {
|
||||
},
|
||||
overflowWrap: 'anywhere',
|
||||
|
||||
'br + br': {
|
||||
display: 'none',
|
||||
},
|
||||
|
||||
strong: {
|
||||
color: 'rgb(var(--slate-12))',
|
||||
fontWeight: '700',
|
||||
|
||||
Reference in New Issue
Block a user