mirror of
https://github.com/discourse/discourse.git
synced 2024-11-21 16:38:15 -06:00
FEATURE: separate admin page for whats new and reports (#26216)
Currently, a new sidebar link for what's new and reports is going to the main dashboard page and activates the proper tab. It might be problematic, especially, when the instance has a lot of problems. In that case, it would be difficult for admin to find reports or what’s new which is rendered at the bottom of the page. Therefore separate pages for reports and what's new were created. Reports were moved to a component that is shared between a separate page and the dashboard.
This commit is contained in:
parent
34a14112a7
commit
043117ca13
@ -0,0 +1,92 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { Input } from "@ember/component";
|
||||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { service } from "@ember/service";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import dIcon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
|
||||
export default class AdminReports extends Component {
|
||||
@service siteSettings;
|
||||
@tracked reports = null;
|
||||
@tracked filter = "";
|
||||
@tracked isLoading = false;
|
||||
|
||||
@bind
|
||||
loadReports() {
|
||||
this.isLoading = true;
|
||||
ajax("/admin/reports")
|
||||
.then((json) => {
|
||||
this.reports = json.reports;
|
||||
})
|
||||
.finally(() => (this.isLoading = false));
|
||||
}
|
||||
|
||||
get filteredReports() {
|
||||
if (!this.reports) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let filteredReports = this.reports;
|
||||
if (this.filter) {
|
||||
const lowerCaseFilter = this.filter.toLowerCase();
|
||||
filteredReports = filteredReports.filter((report) => {
|
||||
return (
|
||||
(report.title || "").toLowerCase().includes(lowerCaseFilter) ||
|
||||
(report.description || "").toLowerCase().includes(lowerCaseFilter)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const hiddenReports = (this.siteSettings.dashboard_hidden_reports || "")
|
||||
.split("|")
|
||||
.filter(Boolean);
|
||||
filteredReports = filteredReports.filter(
|
||||
(report) => !hiddenReports.includes(report.type)
|
||||
);
|
||||
|
||||
return filteredReports;
|
||||
}
|
||||
|
||||
<template>
|
||||
<div {{didInsert this.loadReports}}>
|
||||
<ConditionalLoadingSpinner @condition={{this.isLoading}}>
|
||||
<div class="admin-reports-header">
|
||||
<h2>{{i18n "admin.reports.title"}}</h2>
|
||||
<Input
|
||||
class="admin-reports-header__filter"
|
||||
placeholder={{i18n "admin.filter_reports"}}
|
||||
@value={{this.filter}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
{{dIcon "book"}}
|
||||
{{htmlSafe (i18n "admin.reports.meta_doc")}}
|
||||
</div>
|
||||
|
||||
<ul class="admin-reports-list">
|
||||
{{#each this.filteredReports as |report|}}
|
||||
<li class="admin-reports-list__report">
|
||||
<LinkTo @route="adminReports.show" @model={{report.type}}>
|
||||
<h3
|
||||
class="admin-reports-list__report-title"
|
||||
>{{report.title}}</h3>
|
||||
{{#if report.description}}
|
||||
<p class="admin-reports-list__report-description">
|
||||
{{report.description}}
|
||||
</p>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</ConditionalLoadingSpinner>
|
||||
</div>
|
||||
</template>
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import { service } from "@ember/service";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default class AdminReportsIndexRoute extends DiscourseRoute {
|
||||
@service router;
|
||||
|
||||
beforeModel() {
|
||||
this.router.transitionTo("admin.dashboardReports");
|
||||
}
|
||||
}
|
@ -158,6 +158,7 @@ export default function () {
|
||||
"adminReports",
|
||||
{ path: "/reports", resetNamespace: true },
|
||||
function () {
|
||||
this.route("index", { path: "/" });
|
||||
this.route("show", { path: ":type" });
|
||||
}
|
||||
);
|
||||
@ -223,6 +224,11 @@ export default function () {
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.route("admin.whatsNew", {
|
||||
path: "/whats-new",
|
||||
resetNamespace: true,
|
||||
});
|
||||
});
|
||||
|
||||
// EXPERIMENTAL: These admin routes are hidden behind an `admin_sidebar_enabled_groups`
|
||||
|
@ -1,33 +1 @@
|
||||
<ConditionalLoadingSpinner @condition={{this.isLoading}}>
|
||||
<div class="reports-index section">
|
||||
<div class="section-title">
|
||||
<h2>{{i18n "admin.reports.title"}}</h2>
|
||||
<Input
|
||||
class="filter-reports-input"
|
||||
placeholder={{i18n "admin.dashboard.filter_reports"}}
|
||||
autofocus={{true}}
|
||||
{{on "input" (with-event-value this.filterReports)}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
{{d-icon "book"}}
|
||||
{{html-safe (i18n "admin.reports.meta_doc")}}
|
||||
</div>
|
||||
|
||||
<ul class="reports-list">
|
||||
{{#each this.filteredReports as |report|}}
|
||||
<li class="report">
|
||||
<LinkTo @route="adminReports.show" @model={{report.type}}>
|
||||
<h3 class="report-title">{{report.title}}</h3>
|
||||
{{#if report.description}}
|
||||
<p class="report-description">
|
||||
{{report.description}}
|
||||
</p>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
</ConditionalLoadingSpinner>
|
||||
<AdminReports />
|
@ -0,0 +1 @@
|
||||
<AdminReports />
|
@ -0,0 +1,2 @@
|
||||
<h2>{{i18n "admin.new_features.title"}}</h2>
|
||||
<DashboardNewFeatures />
|
@ -13,7 +13,7 @@ export const ADMIN_NAV_MAP = [
|
||||
},
|
||||
{
|
||||
name: "admin_whats_new",
|
||||
route: "admin.dashboardNewFeatures",
|
||||
route: "admin.whatsNew",
|
||||
label: "admin.account.sidebar_link.whats_new",
|
||||
icon: "gift",
|
||||
},
|
||||
@ -25,7 +25,7 @@ export const ADMIN_NAV_MAP = [
|
||||
links: [
|
||||
{
|
||||
name: "admin_all_reports",
|
||||
route: "admin.dashboardReports",
|
||||
route: "adminReports.index",
|
||||
label: "admin.reports.sidebar_link.all",
|
||||
icon: "chart-bar",
|
||||
},
|
||||
|
@ -1,9 +1,13 @@
|
||||
import { visit } from "@ember/test-helpers";
|
||||
import { click, fillIn, visit } from "@ember/test-helpers";
|
||||
import { test } from "qunit";
|
||||
import { AUTO_GROUPS } from "discourse/lib/constants";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import PreloadStore from "discourse/lib/preload-store";
|
||||
import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
|
||||
import {
|
||||
acceptance,
|
||||
count,
|
||||
exists,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
acceptance("Admin Sidebar - Sections", function (needs) {
|
||||
needs.user({
|
||||
@ -92,6 +96,36 @@ acceptance("Admin Sidebar - Sections", function (needs) {
|
||||
"the admin plugin route is added to the plugins section"
|
||||
);
|
||||
});
|
||||
|
||||
test("Visit reports page", async function (assert) {
|
||||
await visit("/admin");
|
||||
await click(".sidebar-section-link[data-link-name='admin_all_reports']");
|
||||
|
||||
assert.strictEqual(count(".admin-reports-list__report"), 1);
|
||||
|
||||
await fillIn(".admin-reports-header__filter", "flags");
|
||||
|
||||
assert.strictEqual(count(".admin-reports-list__report"), 0);
|
||||
|
||||
await click(
|
||||
".sidebar-section-link[data-link-name='admin_about_your_site']"
|
||||
);
|
||||
await click(".sidebar-section-link[data-link-name='admin_all_reports']");
|
||||
|
||||
assert.strictEqual(
|
||||
count(".admin-reports-list__report"),
|
||||
1,
|
||||
"navigating back and forth resets filter"
|
||||
);
|
||||
|
||||
await fillIn(".admin-reports-header__filter", "activities");
|
||||
|
||||
assert.strictEqual(
|
||||
count(".admin-reports-list__report"),
|
||||
1,
|
||||
"filter is case insensitive"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
acceptance("Admin Sidebar - Sections - Plugin API", function (needs) {
|
||||
|
@ -93,31 +93,25 @@ acceptance("Dashboard", function (needs) {
|
||||
await visit("/admin");
|
||||
await click(".dashboard .navigation-item.reports .navigation-link");
|
||||
|
||||
assert.strictEqual(
|
||||
count(".dashboard .reports-index.section .reports-list .report"),
|
||||
1
|
||||
);
|
||||
assert.strictEqual(count(".dashboard .admin-reports-list__report"), 1);
|
||||
|
||||
await fillIn(".dashboard .filter-reports-input", "flags");
|
||||
await fillIn(".dashboard .admin-reports-header__filter", "flags");
|
||||
|
||||
assert.strictEqual(
|
||||
count(".dashboard .reports-index.section .reports-list .report"),
|
||||
0
|
||||
);
|
||||
assert.strictEqual(count(".dashboard .admin-reports-list__report"), 0);
|
||||
|
||||
await click(".dashboard .navigation-item.security .navigation-link");
|
||||
await click(".dashboard .navigation-item.reports .navigation-link");
|
||||
|
||||
assert.strictEqual(
|
||||
count(".dashboard .reports-index.section .reports-list .report"),
|
||||
count(".dashboard .admin-reports-list__report"),
|
||||
1,
|
||||
"navigating back and forth resets filter"
|
||||
);
|
||||
|
||||
await fillIn(".dashboard .filter-reports-input", "activities");
|
||||
await fillIn(".dashboard .admin-reports-header__filter", "activities");
|
||||
|
||||
assert.strictEqual(
|
||||
count(".dashboard .reports-index.section .reports-list .report"),
|
||||
count(".dashboard .admin-reports-list__report"),
|
||||
1,
|
||||
"filter is case insensitive"
|
||||
);
|
||||
|
@ -13,17 +13,21 @@ acceptance("Reports", function (needs) {
|
||||
test("Visit reports page", async function (assert) {
|
||||
await visit("/admin/reports");
|
||||
|
||||
assert.strictEqual(count(".reports-list .report"), 1);
|
||||
assert.strictEqual(count(".admin-reports-list__report"), 1);
|
||||
|
||||
const report = query(".reports-list .report:first-child");
|
||||
const report = query(".admin-reports-list__report:first-child");
|
||||
|
||||
assert.strictEqual(
|
||||
report.querySelector(".report-title").innerHTML.trim(),
|
||||
report
|
||||
.querySelector(".admin-reports-list__report-title")
|
||||
.innerHTML.trim(),
|
||||
"My report"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
report.querySelector(".report-description").innerHTML.trim(),
|
||||
report
|
||||
.querySelector(".admin-reports-list__report-description")
|
||||
.innerHTML.trim(),
|
||||
"List of my activities"
|
||||
);
|
||||
});
|
||||
|
@ -202,3 +202,51 @@
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-reports-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid var(--primary-low);
|
||||
padding-bottom: 0.5em;
|
||||
|
||||
@media screen and (max-width: 400px) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0.5em 0 0;
|
||||
|
||||
a {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-reports-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
list-style-type: none;
|
||||
margin: 0 -1.5%;
|
||||
&__report {
|
||||
margin: 1.5%;
|
||||
border: 1px solid var(--primary-low);
|
||||
flex: 1 1 28%;
|
||||
transition: box-shadow 0.25s;
|
||||
min-width: 225px;
|
||||
max-width: 550px;
|
||||
a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 1em;
|
||||
.report-description {
|
||||
color: var(--primary-high);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -502,36 +502,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-reports {
|
||||
.reports-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
list-style-type: none;
|
||||
margin: 0 -1.5%;
|
||||
}
|
||||
.report {
|
||||
margin: 1.5%;
|
||||
border: 1px solid var(--primary-low);
|
||||
flex: 1 1 28%;
|
||||
transition: box-shadow 0.25s;
|
||||
min-width: 225px;
|
||||
max-width: 550px;
|
||||
a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 1em;
|
||||
.report-description {
|
||||
color: var(--primary-high);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.version-checks {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
@ -4813,6 +4813,7 @@ en:
|
||||
title: "Discourse Admin"
|
||||
moderator: "Moderator"
|
||||
back_to_forum: "Back to Forum"
|
||||
filter_reports: Filter reports
|
||||
|
||||
tags:
|
||||
remove_muted_tags_from_latest:
|
||||
@ -4827,6 +4828,8 @@ en:
|
||||
sidebar_link:
|
||||
all: "All"
|
||||
|
||||
new_features:
|
||||
title: "What's new"
|
||||
dashboard:
|
||||
title: "Dashboard"
|
||||
last_updated: "Dashboard updated:"
|
||||
@ -4884,7 +4887,6 @@ en:
|
||||
exception_error: Sorry, an error occurred while executing the query
|
||||
too_many_requests: You’ve performed this action too many times. Please wait before trying again.
|
||||
not_found_error: Sorry, this report doesn’t exist
|
||||
filter_reports: Filter reports
|
||||
custom_date_range: Custom date range
|
||||
|
||||
reports:
|
||||
|
@ -325,6 +325,7 @@ Discourse::Application.routes.draw do
|
||||
get "dashboard/security" => "dashboard#security"
|
||||
get "dashboard/reports" => "dashboard#reports"
|
||||
get "dashboard/whats-new" => "dashboard#new_features"
|
||||
get "/whats-new" => "dashboard#new_features"
|
||||
|
||||
resources :dashboard, only: [:index] do
|
||||
collection { get "problems" }
|
||||
|
Loading…
Reference in New Issue
Block a user