0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-11 02:59:07 +08:00
discourse/app/services/admin_dashboard_section_loader.rb
Alan Guo Xiang Tan df5d8cfc05
FIX: Preserve dashboard state across configuration saves
Changing the date range could rebuild a newly enabled dashboard section without its pending configuration state. The section could then request data before the visibility update reached the server and show a fetch error.

Keep every configured section in one canonical state collection and derive the visible order when rendering. This preserves pending, stale, error, and date context while sections are hidden or refreshed.

The section loader now accepts one section ID and returns one result because the endpoint loads sections individually.
2026-07-30 15:19:29 +08:00

80 lines
2.2 KiB
Ruby
Vendored

# frozen_string_literal: true
class AdminDashboardSectionLoader
def self.build(section_id:, current_user:, start_date:, end_date:)
new(
section_id: section_id,
current_user: current_user,
start_date: start_date,
end_date: end_date,
).build
end
def initialize(section_id:, current_user:, start_date:, end_date:)
@section_id = section_id
@current_user = current_user
@start_date = start_date
@end_date = end_date
end
def build
build_section(section_id)
end
private
attr_reader :section_id, :current_user, :start_date, :end_date
def build_section(id)
{ id: id, data: section_data(id, current_user) }
rescue StandardError => e
failed_result(id: id, error: e)
end
def failed_result(result)
Discourse.warn_exception(
result[:error],
message: "Failed to build admin dashboard section",
env: {
section_id: result[:id],
},
)
{ id: result[:id], data: nil, error: true }
end
def section_data(id, user)
case id
when "highlights"
AdminDashboardHighlights.build(start_date: start_date, end_date: end_date)
when "traffic"
AdminDashboardSiteTraffic.build(
start_date: start_date,
end_date: end_date,
guardian: user.guardian,
)
when "engagement"
AdminDashboardEngagement.build(start_date: start_date, end_date: end_date, current_user: user)
when "reports"
reports_section_data(user)
when "search"
AdminDashboardSearch.build(start_date: start_date, end_date: end_date)
else
section = DiscoursePluginRegistry.admin_dashboard_sections.find { |s| s[:id] == id }
section&.dig(:loader)&.call(start_date: start_date, end_date: end_date, current_user: user)
end
end
def reports_section_data(user)
section = AdminDashboard::Reports::Section.build(guardian: user.guardian)
fetched_items =
AdminDashboard::Reports::BulkFetch.call(
items: section[:items],
filters: { start_date:, end_date: }.compact,
guardian: user.guardian,
).fetch(:items)
payloads = fetched_items.index_by { |item| item[:key] }
{ items: section[:items].map { |item| item.merge(payload: payloads.dig(item[:key], :data)) } }
end
end