2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-03-04 01:15:08 +08:00
discourse/app/queries/reports/list_query.rb
chapoi 4ecbaf0699
UX: Admin dashboard & reports improvements (#37598)
* Reorganise the admin reports listing page — reports are now grouped
into logical sections (Engagement, Traffic, Members, Content,
Moderation, Security) instead of a flat alphabetical list.
Plugin-provided reports are grouped under their plugin name.
* Hide legacy/deprecated reports from the listing via a new
Report::LEGACY_REPORTS constant, while keeping them accessible by direct
URL. Legacy reports show an info banner warning they are deprecated.
* Filter out reports from disabled plugins — the reports listing now
resolves which plugin each report belongs to (via source file path) and
excludes reports whose plugin is disabled. Each report also exposes
plugin and plugin_display_name metadata.
* Simplify reports page layout — removed the dashboard settings tab from
the reports page header, replaced hideTabs logic with showHeader that
only renders the DPageHeader on the index route (not on individual
report pages). Removed the separate dashboard-reports
route/controller/template entirely.

Screenshots
<img width="1454" height="1494" alt="Screenshot 2026-02-13 at 12 00
05 pm"
src="https://github.com/user-attachments/assets/60b62563-1ebe-43f6-a13c-695eb640488c"
/>
<img width="1459" height="845" alt="Screenshot 2026-02-13 at 12 00
16 pm"
src="https://github.com/user-attachments/assets/01ed2d0c-4105-4189-b653-f39074a40a43"
/>
<img width="1442" height="1086" alt="Screenshot 2026-02-23 at 2 34
16 pm"
src="https://github.com/user-attachments/assets/b324c67d-b09c-41b5-8840-f5966486eaa8"
/>
2026-02-24 09:20:38 +08:00

124 lines
3.4 KiB
Ruby

# frozen_string_literal: true
module Reports
class ListQuery
class FormattedReport
attr_reader :type, :name
def initialize(name)
@name = name
@type = name.to_s.gsub("report_", "")
end
def visible?(admin:)
return false if Report.hidden?(type, admin:)
if SiteSetting.reporting_improvements
return false if plugin_report? && plugin_disabled?
return false if Report::LEGACY_REPORTS.include?(type)
end
true
end
def to_h
result = {
type:,
title:,
description:,
description_link: I18n.t("reports.#{type}.description_link", default: "").presence,
}
if SiteSetting.reporting_improvements && plugin_report?
result[:plugin] = plugin_name
result[:plugin_display_name] = plugin_display_name
end
result
end
private
# HACK: We need to show a different label and description for some
# old reports while people are still relying on them, that lets us
# point toward the new 'Site traffic' report as well. Not ideal,
# but apart from duplicating the report there's not a nicer way to do this.
def title
return I18n.t("reports.#{type}.title_legacy") if legacy?
I18n.t("reports.#{type}.title")
end
def description
return I18n.t("reports.#{type}.description_legacy") if legacy?
I18n.t("reports.#{type}.description", default: "").presence
end
def legacy?
SiteSetting.use_legacy_pageviews &&
type.in?(%w[consolidated_page_views consolidated_page_views_browser_detection])
end
def plugin_name
return @plugin_name if defined?(@plugin_name)
@plugin_name = resolve_plugin_name
end
def resolve_plugin_name
return unless Report.singleton_class.method_defined?(@name)
source_path = Report.method(@name).source_location.first
return unless source_path&.include?("/plugins/")
# Extract plugin name from path like /plugins/discourse-ai/...
match = source_path.match(%r{/plugins/([^/]+)/})
match[1] if match
rescue NameError
nil
end
def plugin_instance
return @plugin_instance if defined?(@plugin_instance)
@plugin_instance = plugin_name && Discourse.plugins_by_name[plugin_name]
end
def plugin_display_name
plugin_instance&.humanized_name
end
def plugin_report?
plugin_name.present?
end
def plugin_disabled?
return true unless plugin_instance
!plugin_instance.enabled?
end
end
def self.call(admin:)
page_view_req_report_methods =
["page_view_total_reqs"] +
ApplicationRequest
.req_types
.keys
.select { |r| r =~ /\Apage_view_/ && r !~ /mobile/ }
.map { |r| r + "_reqs" }
if !SiteSetting.use_legacy_pageviews
page_view_req_report_methods << "page_view_legacy_total_reqs"
end
reports_methods =
page_view_req_report_methods +
Report.singleton_methods.grep(/\Areport_(?!about|storage_stats)/)
reports_methods
.filter_map do |report_name|
report = Reports::ListQuery::FormattedReport.new(report_name)
report.to_h if report.visible?(admin:)
end
.sort_by { |report| report[:title] }
end
end
end