diff --git a/app/controllers/api/v1/accounts/portals_controller.rb b/app/controllers/api/v1/accounts/portals_controller.rb
index 7f43766d2..b12f89c0a 100644
--- a/app/controllers/api/v1/accounts/portals_controller.rb
+++ b/app/controllers/api/v1/accounts/portals_controller.rb
@@ -23,7 +23,7 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController
def update
ActiveRecord::Base.transaction do
- @portal.update!(portal_params.merge(live_chat_widget_params)) if params[:portal].present?
+ @portal.update!(merged_portal_params.merge(live_chat_widget_params)) if params[:portal].present?
# @portal.custom_domain = parsed_custom_domain
process_attached_logo if params[:blob_id].present?
rescue ActiveRecord::RecordInvalid => e
@@ -79,10 +79,20 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController
def portal_params
params.require(:portal).permit(
:id, :color, :custom_domain, :header_text, :homepage_link,
- :name, :page_title, :slug, :archived, { config: [:default_locale, { allowed_locales: [] }] }
+ :name, :page_title, :slug, :archived, { config: [:default_locale, :show_author, { allowed_locales: [] }] }
)
end
+ def merged_portal_params
+ update_params = portal_params.to_h
+ if update_params.key?('config')
+ base_config = @portal.config.is_a?(Hash) ? @portal.config : {}
+ incoming_config = update_params['config']
+ update_params['config'] = incoming_config.is_a?(Hash) ? base_config.merge(incoming_config) : base_config
+ end
+ update_params
+ end
+
def live_chat_widget_params
permitted_params = params.permit(:inbox_id)
return {} unless permitted_params.key?(:inbox_id)
diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue
index b99f08a29..3258162dc 100644
--- a/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue
+++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue
@@ -15,6 +15,7 @@ import Input from 'dashboard/components-next/input/Input.vue';
import Avatar from 'dashboard/components-next/avatar/Avatar.vue';
import ComboBox from 'dashboard/components-next/combobox/ComboBox.vue';
import ColorPicker from 'dashboard/components-next/colorpicker/ColorPicker.vue';
+import Switch from 'dashboard/components-next/switch/Switch.vue';
const props = defineProps({
activePortal: {
@@ -45,6 +46,7 @@ const state = reactive({
liveChatWidgetInboxId: '',
logoUrl: '',
avatarBlobId: '',
+ showAuthor: true,
});
const originalState = reactive({ ...state });
@@ -117,6 +119,7 @@ watch(
homePageLink: newVal.homepage_link,
slug: newVal.slug,
liveChatWidgetInboxId: newVal.inbox?.id || '',
+ showAuthor: newVal.config?.show_author !== false,
});
if (newVal.logo) {
const {
@@ -149,6 +152,7 @@ const handleUpdatePortal = () => {
homepage_link: state.homePageLink,
blob_id: state.avatarBlobId,
inbox_id: state.liveChatWidgetInboxId,
+ config: { show_author: state.showAuthor },
};
emit('updatePortal', portal);
};
@@ -335,6 +339,21 @@ const handleAvatarDelete = () => {
+
+
+
+
+
+ {{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.SHOW_AUTHOR.HELP_TEXT') }}
+
+
+
+ <% if portal.show_author? %>
<%= render "public/api/v1/portals/authors", category: category, show_expanded: false %>
•
+ <% end %>
<%= render 'public/api/v1/portals/article_count', article_count: category.articles.published.order(position: :asc).size %>
diff --git a/app/views/public/api/v1/portals/categories/_category-hero.html.erb b/app/views/public/api/v1/portals/categories/_category-hero.html.erb
index c0b87d82c..d2f808ce5 100644
--- a/app/views/public/api/v1/portals/categories/_category-hero.html.erb
+++ b/app/views/public/api/v1/portals/categories/_category-hero.html.erb
@@ -22,8 +22,10 @@
<% end %>
+ <% if portal.show_author? %>
<%= render "public/api/v1/portals/authors", category: category, show_expanded: true %>
•
+ <% end %>
<%= render 'public/api/v1/portals/article_count', article_count: category.articles.published.size %>
diff --git a/spec/controllers/api/v1/accounts/portals_controller_spec.rb b/spec/controllers/api/v1/accounts/portals_controller_spec.rb
index c8e5c653c..a21be237a 100644
--- a/spec/controllers/api/v1/accounts/portals_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/portals_controller_spec.rb
@@ -131,7 +131,49 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do
json_response = response.parsed_body
expect(json_response['name']).to eql(portal_params[:portal][:name])
expect(json_response['config']).to eql({ 'allowed_locales' => [{ 'articles_count' => 0, 'categories_count' => 0, 'code' => 'en' },
- { 'articles_count' => 0, 'categories_count' => 0, 'code' => 'es' }] })
+ { 'articles_count' => 0, 'categories_count' => 0, 'code' => 'es' }],
+ 'show_author' => true })
+ end
+
+ it 'persists show_author as false when explicitly set' do
+ put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}",
+ params: { portal: { config: { show_author: false } } },
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['config']['show_author']).to be(false)
+
+ portal.reload
+ expect(portal.show_author?).to be(false)
+ end
+
+ it 'preserves show_author when updating other portal fields' do
+ portal.update!(config: portal.config.merge('show_author' => false))
+
+ put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}",
+ params: { portal: { name: 'renamed_portal' } },
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ portal.reload
+ expect(portal.show_author?).to be(false)
+ expect(portal.name).to eql('renamed_portal')
+ end
+
+ it 'preserves show_author when updating only allowed_locales' do
+ portal.update!(config: portal.config.merge('show_author' => false))
+
+ put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}",
+ params: { portal: { config: { allowed_locales: %w[en fr] } } },
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ portal.reload
+ expect(portal.show_author?).to be(false)
end
it 'archive portal' do