discourse/app/assets/javascripts/admin/addon/components/admin-reports.gjs
Kris c544d7afa0
FEATURE: Add reusable AdminFilterControls component, apply to plugins and reports (#33706)
This creates a reusable filter component for admin areas that includes a
text filter and dropdown.

The component accepts an array to filter, an array of searchable
property names, and optional dropdown values. An additional option is
available if you'd like the default dropdown value to be something other
than "all."




It looks like this: 

<img width="2204" height="876" alt="image"
src="https://github.com/user-attachments/assets/48693634-6752-4078-8639-42fb1ac77679"
/>

The dropdown is optional, without it: 

<img width="1756" height="900" alt="image"
src="https://github.com/user-attachments/assets/ea9a7ae6-ae86-4cd8-8b99-4afc082f2ce5"
/>

When there are no matching filters, it provides a reset button: 

<img width="2188" height="394" alt="image"
src="https://github.com/user-attachments/assets/9e59218e-8040-41db-ba81-1dc2628429bb"
/>

I'll use this to replace occurrences of similar filters, we've added a
couple to the AI plugin.
2025-07-24 16:55:33 -04:00

57 lines
1.9 KiB
Text

import Component from "@glimmer/component";
import { array } from "@ember/helper";
import { service } from "@ember/service";
import AsyncContent from "discourse/components/async-content";
import { ajax } from "discourse/lib/ajax";
import { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import AdminFilterControls from "admin/components/admin-filter-controls";
import AdminSectionLandingItem from "admin/components/admin-section-landing-item";
import AdminSectionLandingWrapper from "admin/components/admin-section-landing-wrapper";
export default class AdminReports extends Component {
@service siteSettings;
@bind
async loadReports() {
const response = await ajax("/admin/reports");
return response.reports;
}
@bind
filterReports(reports) {
if (!reports) {
return [];
}
const hiddenReports = (this.siteSettings.dashboard_hidden_reports || "")
.split("|")
.filter(Boolean);
return reports.filter((report) => !hiddenReports.includes(report.type));
}
<template>
<AsyncContent @asyncData={{this.loadReports}}>
<:content as |reports|>
<AdminFilterControls
@array={{this.filterReports reports}}
@searchableProps={{array "title" "description"}}
@inputPlaceholder={{i18n "admin.filter_reports"}}
@noResultsMessage={{i18n "admin.filter_reports_no_results"}}
as |filteredReports|
>
<AdminSectionLandingWrapper class="admin-reports-list">
{{#each filteredReports as |report|}}
<AdminSectionLandingItem
@titleLabelTranslated={{report.title}}
@descriptionLabelTranslated={{report.description}}
@titleRoute="adminReports.show"
@titleRouteModel={{report.type}}
/>
{{/each}}
</AdminSectionLandingWrapper>
</AdminFilterControls>
</:content>
</AsyncContent>
</template>
}