feat: Link help center portal to an Inbox (#6903)

This commit is contained in:
Tejaswini Chile
2023-04-24 12:49:52 +05:30
committed by GitHub
parent f825a22997
commit e3193dcabc
11 changed files with 77 additions and 3 deletions

View File

@@ -114,10 +114,13 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
def inbox_attributes
[:name, :avatar, :greeting_enabled, :greeting_message, :enable_email_collect, :csat_survey_enabled,
:enable_auto_assignment, :working_hours_enabled, :out_of_office_message, :timezone, :allow_messages_after_resolved,
:lock_to_single_conversation]
:lock_to_single_conversation, :portal_id]
end
def permitted_params(channel_attributes = [])
# We will remove this line after fixing https://linear.app/chatwoot/issue/CW-1567/null-value-passed-as-null-string-to-backend
params.each { |k, v| params[k] = params[k] == 'null' ? nil : v }
params.permit(
*inbox_attributes,
channel: [:type, *channel_attributes]

View File

@@ -482,6 +482,13 @@
"WHATSAPP_WEBHOOK_SUBHEADER": "This token is used to verify the authenticity of the webhook endpoint.",
"UPDATE_PRE_CHAT_FORM_SETTINGS": "Update Pre Chat Form Settings"
},
"HELP_CENTER": {
"LABEL": "Help Center",
"PLACEHOLDER": "Select Help Center",
"SELECT_PLACEHOLDER": "Select Help Center",
"REMOVE": "Remove Help Center",
"SUB_TEXT": "Attach a Help Center with the inbox"
},
"AUTO_ASSIGNMENT": {
"MAX_ASSIGNMENT_LIMIT": "Auto assignment limit",
"MAX_ASSIGNMENT_LIMIT_RANGE_ERROR": "Please enter a value greater than 0",

View File

@@ -258,7 +258,22 @@
}}
</p>
</label>
<div class="medium-9 settings-item settings-item">
<label>
{{ $t('INBOX_MGMT.HELP_CENTER.LABEL') }}
</label>
<select v-model="selectedPortalSlug" class="filter__question">
<option value="">
{{ $t('INBOX_MGMT.HELP_CENTER.PLACEHOLDER') }}
</option>
<option v-for="p in portals" :key="p.slug" :value="p.slug">
{{ p.name }}
</option>
</select>
<p class="help-text">
{{ $t('INBOX_MGMT.HELP_CENTER.SUB_TEXT') }}
</p>
</div>
<label
v-if="canLocktoSingleConversation"
class="medium-9 columns settings-item"
@@ -425,6 +440,7 @@ export default {
selectedFeatureFlags: [],
replyTime: '',
selectedTabIndex: 0,
selectedPortalSlug: '',
};
},
computed: {
@@ -432,6 +448,7 @@ export default {
accountId: 'getCurrentAccountId',
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
uiFlags: 'inboxes/getUIFlags',
portals: 'portals/allPortals',
}),
selectedTabKey() {
return this.tabs[this.selectedTabIndex]?.key;
@@ -519,6 +536,7 @@ export default {
inbox() {
return this.$store.getters['inboxes/getInbox'](this.currentInboxId);
},
inboxName() {
if (this.isATwilioSMSChannel || this.isATwilioWhatsAppChannel) {
return `${this.inbox.name} (${this.inbox.messaging_service_sid ||
@@ -566,8 +584,12 @@ export default {
},
mounted() {
this.fetchInboxSettings();
this.fetchPortals();
},
methods: {
fetchPortals() {
this.$store.dispatch('portals/index');
},
handleFeatureFlag(e) {
this.selectedFeatureFlags = this.toggleInput(
this.selectedFeatureFlags,
@@ -607,6 +629,9 @@ export default {
this.selectedFeatureFlags = this.inbox.selected_feature_flags || [];
this.replyTime = this.inbox.reply_time;
this.locktoSingleConversation = this.inbox.lock_to_single_conversation;
this.selectedPortalSlug = this.inbox.help_center
? this.inbox.help_center.slug
: '';
});
},
async updateInbox() {
@@ -619,6 +644,11 @@ export default {
allow_messages_after_resolved: this.allowMessagesAfterResolved,
greeting_enabled: this.greetingEnabled,
greeting_message: this.greetingMessage || '',
portal_id: this.selectedPortalSlug
? this.portals.find(
portal => portal.slug === this.selectedPortalSlug
).id
: null,
lock_to_single_conversation: this.locktoSingleConversation,
channel: {
widget_color: this.inbox.widget_color,

View File

@@ -23,11 +23,17 @@
# updated_at :datetime not null
# account_id :integer not null
# channel_id :integer not null
# portal_id :bigint
#
# Indexes
#
# index_inboxes_on_account_id (account_id)
# index_inboxes_on_channel_id_and_channel_type (channel_id,channel_type)
# index_inboxes_on_portal_id (portal_id)
#
# Foreign Keys
#
# fk_rails_... (portal_id => portals.id)
#
class Inbox < ApplicationRecord
@@ -45,6 +51,7 @@ class Inbox < ApplicationRecord
validate :ensure_valid_max_assignment_limit
belongs_to :account
belongs_to :portal, optional: true
belongs_to :channel, polymorphic: true, dependent: :destroy

View File

@@ -37,6 +37,7 @@ class Portal < ApplicationRecord
dependent: :nullify,
source: :user
has_one_attached :logo
has_many :inboxes, dependent: :nullify
before_validation -> { normalize_empty_string_to_nil(%i[custom_domain homepage_link]) }
validates :account_id, presence: true

View File

@@ -17,6 +17,13 @@ json.callback_webhook_url resource.callback_webhook_url
json.allow_messages_after_resolved resource.allow_messages_after_resolved
json.lock_to_single_conversation resource.lock_to_single_conversation
if resource.portal.present?
json.help_center do
json.name resource.portal.name
json.slug resource.portal.slug
end
end
## Channel specific settings
## TODO : Clean up and move the attributes into channel sub section

View File

@@ -0,0 +1,5 @@
class AddPortalIdToInbox < ActiveRecord::Migration[6.1]
def change
add_reference :inboxes, :portal, foreign_key: true
end
end

View File

@@ -579,8 +579,10 @@ ActiveRecord::Schema.define(version: 2023_04_18_100944) do
t.boolean "allow_messages_after_resolved", default: true
t.jsonb "auto_assignment_config", default: {}
t.boolean "lock_to_single_conversation", default: false, null: false
t.bigint "portal_id"
t.index ["account_id"], name: "index_inboxes_on_account_id"
t.index ["channel_id", "channel_type"], name: "index_inboxes_on_channel_id_and_channel_type"
t.index ["portal_id"], name: "index_inboxes_on_portal_id"
end
create_table "installation_configs", force: :cascade do |t|
@@ -918,6 +920,7 @@ ActiveRecord::Schema.define(version: 2023_04_18_100944) do
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "inboxes", "portals"
create_trigger("accounts_after_insert_row_tr", :generated => true, :compatibility => 1).
on("accounts").
after(:insert).

View File

@@ -429,7 +429,8 @@ RSpec.describe 'Inboxes API', type: :request do
context 'when it is an authenticated user' do
let(:admin) { create(:user, account: account, role: :administrator) }
let(:valid_params) { { name: 'new test inbox', enable_auto_assignment: false } }
let!(:portal) { create(:portal, account_id: account.id) }
let(:valid_params) { { name: 'new test inbox', enable_auto_assignment: false, portal_id: portal.id } }
it 'will not update inbox for agent' do
agent = create(:user, account: account, role: :agent)
@@ -450,6 +451,7 @@ RSpec.describe 'Inboxes API', type: :request do
expect(response).to have_http_status(:success)
expect(inbox.reload.enable_auto_assignment).to be_falsey
expect(inbox.reload.portal_id).to eq(portal.id)
expect(JSON.parse(response.body)['name']).to eq 'new test inbox'
end

View File

@@ -197,11 +197,19 @@ RSpec.describe Inbox do
describe '#update' do
let!(:inbox) { FactoryBot.create(:inbox) }
let!(:portal) { FactoryBot.create(:portal) }
before do
allow(Rails.configuration.dispatcher).to receive(:dispatch)
end
it 'set portal id in inbox' do
inbox.portal_id = portal.id
inbox.save
expect(inbox.portal).to eq(portal)
end
it 'resets cache key if there is an update in the channel' do
channel = inbox.channel
channel.update(widget_color: '#fff')

View File

@@ -14,6 +14,7 @@ RSpec.describe Portal, type: :model do
it { is_expected.to have_many(:articles) }
it { is_expected.to have_many(:portal_members) }
it { is_expected.to have_many(:members) }
it { is_expected.to have_many(:inboxes) }
end
describe 'validations' do