UX: Adding reports dashboard tab, new layout, report descriptions (#6790)

Co-Authored-By: Kris  <shout@k-ris.com>
This commit is contained in:
Joffrey JAFFEUX 2018-12-19 14:44:43 +01:00 committed by GitHub
parent e5fd018f44
commit e655e1863f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 214 additions and 63 deletions

View File

@ -0,0 +1,30 @@
import computed from "ember-addons/ember-computed-decorators";
const { get } = Ember;
export default Ember.Controller.extend({
filter: null,
@computed("model.[]", "filter")
filterReports(reports, filter) {
if (filter) {
filter = filter.toLowerCase();
return reports.filter(report => {
return (
(get(report, "title") || "").toLowerCase().indexOf(filter) > -1 ||
(get(report, "description") || "").toLowerCase().indexOf(filter) > -1
);
});
}
return reports;
},
actions: {
filterReports(filter) {
Ember.run.debounce(this, this._performFiltering, filter, 250);
}
},
_performFiltering(filter) {
this.set("filter", filter);
}
});

View File

@ -0,0 +1,11 @@
import { ajax } from "discourse/lib/ajax";
export default Discourse.Route.extend({
model() {
return ajax("/admin/reports").then(json => json);
},
setupController(controller, model) {
controller.setProperties({ model: model.reports, filter: null });
}
});

View File

@ -1,13 +1,5 @@
import { ajax } from "discourse/lib/ajax";
export default Discourse.Route.extend({ export default Discourse.Route.extend({
model() { beforeModel() {
return ajax("/admin/reports").then(json => { this.transitionTo("admin.dashboardNextReports");
return json;
});
},
setupController(controller, model) {
controller.setProperties({ model: model.reports });
} }
}); });

View File

@ -12,6 +12,10 @@ export default function() {
path: "/dashboard/security", path: "/dashboard/security",
resetNamespace: true resetNamespace: true
}); });
this.route("admin.dashboardNextReports", {
path: "/dashboard/reports",
resetNamespace: true
});
}); });
this.route( this.route(

View File

@ -6,7 +6,7 @@
<ul class="breadcrumb"> <ul class="breadcrumb">
{{#if showAllReportsLink}} {{#if showAllReportsLink}}
<li class="item all-reports"> <li class="item all-reports">
{{#link-to "adminReports" class="report-url"}} {{#link-to "admin.dashboardNextReports" class="report-url"}}
{{i18n "admin.dashboard.all_reports"}} {{i18n "admin.dashboard.all_reports"}}
{{/link-to}} {{/link-to}}
</li> </li>

View File

@ -26,6 +26,11 @@
{{i18n "admin.dashboard.security_tab"}} {{i18n "admin.dashboard.security_tab"}}
{{/link-to}} {{/link-to}}
</li> </li>
<li class="navigation-item reports">
{{#link-to "admin.dashboardNextReports" class="navigation-link"}}
{{i18n "admin.dashboard.reports_tab"}}
{{/link-to}}
</li>
</ul> </ul>
{{outlet}} {{outlet}}

View File

@ -5,7 +5,7 @@
<div class="period-section"> <div class="period-section">
<div class="section-title"> <div class="section-title">
<h2> <h2>
<a href="{{get-url '/admin/reports'}}"> <a href="{{get-url '/admin/dashboard/reports'}}">
{{i18n "admin.dashboard.community_health"}} {{i18n "admin.dashboard.community_health"}}
</a> </a>
</h2> </h2>
@ -85,10 +85,6 @@
{{/each}} {{/each}}
</div> </div>
</div> </div>
{{#link-to "adminReports"}}
{{i18n "admin.dashboard.all_reports"}}
{{/link-to}}
</div> </div>
<div class="user-metrics"> <div class="user-metrics">

View File

@ -4,7 +4,7 @@
<div class="moderators-activity section"> <div class="moderators-activity section">
<div class="section-title"> <div class="section-title">
<h2> <h2>
<a href="{{get-url '/admin/reports/moderators_activity'}}"> <a href="{{get-url '/admin/dashboard/reports/moderators_activity'}}">
{{i18n "admin.dashboard.moderators_activity"}} {{i18n "admin.dashboard.moderators_activity"}}
</a> </a>
</h2> </h2>

View File

@ -0,0 +1,28 @@
{{#conditional-loading-spinner condition=isLoading}}
<div class="reports-index section">
<div class="section-title">
<h2>{{i18n "admin.reports.title"}}</h2>
{{input
class="filter-reports-input"
input=(action "filterReports" value="target.value")
placeholder=(i18n "admin.dashboard.filter_reports")
autofocus=true}}
</div>
<ul class="reports-list">
{{#each filterReports as |report|}}
<li class="report">
{{#link-to 'adminReports.show' report.type}}
<h3 class="report-title">{{report.title}}</h3>
{{#if report.description}}
<p class="report-description">
{{report.description}}
</p>
{{/if}}
{{/link-to}}
</li>
{{/each}}
</ul>
</div>
{{/conditional-loading-spinner}}

View File

@ -1,17 +0,0 @@
<h3>{{i18n "admin.reports.title"}}</h3>
<ul class="reports-list">
{{#each model as |report|}}
<li class="report">
{{#link-to 'adminReports.show' report.type}}
<h4 class="report-title">{{report.title}}</h4>
{{/link-to}}
{{#if report.description}}
<p class="report-description">
{{report.description}}
</p>
{{/if}}
</li>
{{/each}}
</ul>

View File

@ -13,20 +13,6 @@
height: 400px; height: 400px;
} }
.reports-list {
list-style: none;
margin: 0;
.report {
padding-bottom: 0.5em;
margin-bottom: 0.5em;
.report-description {
margin: 0;
}
}
}
.report-container { .report-container {
display: flex; display: flex;

View File

@ -43,6 +43,10 @@
@include active-navigation-item; @include active-navigation-item;
} }
&.dashboard-next-reports .navigation-item.reports {
@include active-navigation-item;
}
&.general .navigation-item.general { &.general .navigation-item.general {
@include active-navigation-item; @include active-navigation-item;
} }
@ -526,3 +530,33 @@
grid-row-gap: 1em; grid-row-gap: 1em;
} }
} }
.dashboard-next-reports {
.reports-list {
display: flex;
flex-wrap: wrap;
list-style-type: none;
margin: 0 -1.5%;
}
.report {
margin: 1.5%;
border: 1px solid $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: $primary-high;
}
}
&:hover {
box-shadow: shadow("card");
}
}
}

View File

@ -11,6 +11,7 @@ class Admin::DashboardNextController < Admin::AdminController
def moderation; end def moderation; end
def security; end def security; end
def reports; end
def general def general
render json: AdminDashboardNextGeneralData.fetch_cached_stats render json: AdminDashboardNextGeneralData.fetch_cached_stats

View File

@ -2861,11 +2861,13 @@ en:
general_tab: "General" general_tab: "General"
moderation_tab: "Moderation" moderation_tab: "Moderation"
security_tab: "Security" security_tab: "Security"
reports_tab: "Reports"
disabled: Disabled disabled: Disabled
timeout_error: Sorry, query is taking too long, please pick a shorter interval timeout_error: Sorry, query is taking too long, please pick a shorter interval
exception_error: Sorry, an error occurred while executing the query exception_error: Sorry, an error occurred while executing the query
too_many_requests: Youve performed this action too many times. Please wait before trying again. too_many_requests: Youve performed this action too many times. Please wait before trying again.
not_found_error: Sorry, this report doesnt exist not_found_error: Sorry, this report doesnt exist
filter_reports: Filter reports
reports: reports:
trend_title: "%{percent} change. Currently %{current}, was %{prev} in previous period." trend_title: "%{percent} change. Currently %{current}, was %{prev} in previous period."

View File

@ -900,6 +900,7 @@ en:
editor: Editor editor: Editor
author: Author author: Author
edit_reason: Reason edit_reason: Reason
description: "Number of new post edits."
user_flagging_ratio: user_flagging_ratio:
title: "User Flagging Ratio" title: "User Flagging Ratio"
labels: labels:
@ -908,6 +909,7 @@ en:
disagreed_flags: Disagreed flags disagreed_flags: Disagreed flags
ignored_flags: Ignored flags ignored_flags: Ignored flags
score: Score score: Score
description: "List of users ordered by ratio of staff response to their flags (disagreed to agreed)."
moderators_activity: moderators_activity:
title: "Moderator Activity" title: "Moderator Activity"
labels: labels:
@ -918,6 +920,7 @@ en:
post_count: Posts created post_count: Posts created
pm_count: PMs created pm_count: PMs created
revision_count: Revisions revision_count: Revisions
description: List of moderator activity including flags reviewed, reading time, topics created, posts created, private messages created, and revisions.
flags_status: flags_status:
title: "Flags Status" title: "Flags Status"
values: values:
@ -931,39 +934,42 @@ en:
poster: Poster poster: Poster
flagger: Flagger flagger: Flagger
time_to_resolution: Resolution time time_to_resolution: Resolution time
description: "List of flags' statuses including type of flag, poster, flagger, and time to resolution."
visits: visits:
title: "User Visits" title: "User Visits"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of visits" yaxis: "Number of visits"
description: "Number of all unique user visits."
signups: signups:
title: "Signups" title: "Signups"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of signups" yaxis: "Number of signups"
description: "New account registrations for this period" description: "New account registrations for this period."
new_contributors: new_contributors:
title: "New Contributors" title: "New Contributors"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of new contributors" yaxis: "Number of new contributors"
description: "Number of users who made their first post during this period" description: "Number of users who made their first post during this period."
dau_by_mau: dau_by_mau:
title: "DAU/MAU" title: "DAU/MAU"
xaxis: "Day" xaxis: "Day"
yaxis: "DAU/MAU" yaxis: "DAU/MAU"
description: "No. of members that logged in in the last day divided by no of members that logged in in the last month returns a % which indicates community 'stickiness'. Aim for >30%." description: "Number of members that logged in in the last day divided by number of members that logged in in the last month returns a % which indicates community 'stickiness'. Aim for >30%."
daily_engaged_users: daily_engaged_users:
title: "Daily Engaged Users" title: "Daily Engaged Users"
xaxis: "Day" xaxis: "Day"
yaxis: "Engaged Users" yaxis: "Engaged Users"
description: "Number of users that have liked or posted in the last day" description: "Number of users that have liked or posted in the last day."
profile_views: profile_views:
title: "User Profile Views" title: "User Profile Views"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of user profiles viewed" yaxis: "Number of user profiles viewed"
description: "Total new views of user profiles."
topics: topics:
title: "Topics" title: "Topics"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of new topics" yaxis: "Number of new topics"
description: "New topics created during this period" description: "New topics created during this period."
posts: posts:
title: "Posts" title: "Posts"
xaxis: "Day" xaxis: "Day"
@ -973,24 +979,24 @@ en:
title: "Likes" title: "Likes"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of new likes" yaxis: "Number of new likes"
description: "Number of new likes."
flags: flags:
title: "Flags" title: "Flags"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of flags" yaxis: "Number of flags"
description: "Number of new flags."
bookmarks: bookmarks:
title: "Bookmarks" title: "Bookmarks"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of new bookmarks" yaxis: "Number of new bookmarks"
starred: description: "Number of new topics and posts bookmarked."
title: "Starred"
xaxis: "Day"
yaxis: "Number of new starred topics"
users_by_trust_level: users_by_trust_level:
title: "Users per Trust Level" title: "Users per Trust Level"
xaxis: "Trust Level" xaxis: "Trust Level"
yaxis: "Number of Users" yaxis: "Number of Users"
labels: labels:
level: Level level: Level
description: "Number of users grouped by trust level."
users_by_type: users_by_type:
title: "Users per Type" title: "Users per Type"
xaxis: "Type" xaxis: "Type"
@ -1002,40 +1008,49 @@ en:
moderator: Moderator moderator: Moderator
suspended: Suspended suspended: Suspended
silenced: Silenced silenced: Silenced
description: "Number of users grouped by admin, moderator, suspended, and silenced."
trending_search: trending_search:
title: Trending Search Terms title: Trending Search Terms
labels: labels:
term: Term term: Term
searches: Searches searches: Searches
click_through: CTR click_through: CTR
description: "Most popular search terms with their click-through rates."
emails: emails:
title: "Emails Sent" title: "Emails Sent"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of Emails" yaxis: "Number of Emails"
description: "Number of new emails sent."
user_to_user_private_messages: user_to_user_private_messages:
title: "User-to-User (excluding replies)" title: "User-to-User (excluding replies)"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of messages" yaxis: "Number of messages"
description: "Number of newly initiated private messages."
user_to_user_private_messages_with_replies: user_to_user_private_messages_with_replies:
title: "User-to-User (with replies)" title: "User-to-User (with replies)"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of messages" yaxis: "Number of messages"
description: "Number of all new private messages and responses."
system_private_messages: system_private_messages:
title: "System" title: "System"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of messages" yaxis: "Number of messages"
description: "Number of private messages sent automatically by the System."
moderator_warning_private_messages: moderator_warning_private_messages:
title: "Moderator Warning" title: "Moderator Warning"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of messages" yaxis: "Number of messages"
description: "Number of warnings sent by private messages from moderators."
notify_moderators_private_messages: notify_moderators_private_messages:
title: "Notify Moderators" title: "Notify Moderators"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of messages" yaxis: "Number of messages"
description: "Number of times moderators have been privately notified by a flag."
notify_user_private_messages: notify_user_private_messages:
title: "Notify User" title: "Notify User"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of messages" yaxis: "Number of messages"
description: "Number of times users have been privately notified by a flag."
top_referrers: top_referrers:
title: "Top Referrers" title: "Top Referrers"
xaxis: "User" xaxis: "User"
@ -1045,6 +1060,7 @@ en:
user: "User" user: "User"
num_clicks: "Clicks" num_clicks: "Clicks"
num_topics: "Topics" num_topics: "Topics"
description: "Users listed by number of clicks on links they have shared."
top_traffic_sources: top_traffic_sources:
title: "Top Traffic Sources" title: "Top Traffic Sources"
xaxis: "Domain" xaxis: "Domain"
@ -1055,35 +1071,43 @@ en:
domain: Domain domain: Domain
num_clicks: Clicks num_clicks: Clicks
num_topics: Topics num_topics: Topics
description: "External sources that have linked to this site the most."
top_referred_topics: top_referred_topics:
title: "Top Referred Topics" title: "Top Referred Topics"
labels: labels:
num_clicks: "Clicks" num_clicks: "Clicks"
topic: "Topic" topic: "Topic"
description: "Topics that have received the most clicks from external sources."
page_view_anon_reqs: page_view_anon_reqs:
title: "Anonymous" title: "Anonymous"
xaxis: "Day" xaxis: "Day"
yaxis: "Anonymous Pageviews" yaxis: "Anonymous Pageviews"
description: "Number of new page views by visitors not logged in to an account."
page_view_logged_in_reqs: page_view_logged_in_reqs:
title: "Logged In" title: "Logged In"
xaxis: "Day" xaxis: "Day"
yaxis: "Logged In Pageviews" yaxis: "Logged In Pageviews"
description: "Number of new pageviews from logged in users."
page_view_crawler_reqs: page_view_crawler_reqs:
title: "Web Crawlers" title: "Web Crawlers"
xaxis: "Day" xaxis: "Day"
yaxis: "Web Crawler Pageviews" yaxis: "Web Crawler Pageviews"
description: "Webcrawlers listed by number of new pageviews they have generated."
page_view_total_reqs: page_view_total_reqs:
title: "Pageviews" title: "Pageviews"
xaxis: "Day" xaxis: "Day"
yaxis: "Total Pageviews" yaxis: "Total Pageviews"
description: "Number of new pageviews from all visitors."
page_view_logged_in_mobile_reqs: page_view_logged_in_mobile_reqs:
title: "Logged In Pageviews" title: "Logged In Pageviews"
xaxis: "Day" xaxis: "Day"
yaxis: "Mobile Logged In Pageviews" yaxis: "Mobile Logged In Pageviews"
description: "Number of new pageviews from users on mobile devices and logged in to an account."
page_view_anon_mobile_reqs: page_view_anon_mobile_reqs:
title: "Anon Pageviews" title: "Anon Pageviews"
xaxis: "Day" xaxis: "Day"
yaxis: "Mobile Anon Pageviews" yaxis: "Mobile Anon Pageviews"
description: "Number of new pageviews from visitors on a mobile device who are not logged in."
http_background_reqs: http_background_reqs:
title: "Background" title: "Background"
xaxis: "Day" xaxis: "Day"
@ -1112,19 +1136,23 @@ en:
title: "Time to first response" title: "Time to first response"
xaxis: "Day" xaxis: "Day"
yaxis: "Average time (hours)" yaxis: "Average time (hours)"
description: "Average time (in hours) of the first response to new topics."
topics_with_no_response: topics_with_no_response:
title: "Topics with no response" title: "Topics with no response"
xaxis: "Day" xaxis: "Day"
yaxis: "Total" yaxis: "Total"
description: "Number of new topics created that did not receive a response."
mobile_visits: mobile_visits:
title: "User Visits (mobile)" title: "User Visits (mobile)"
xaxis: "Day" xaxis: "Day"
yaxis: "Number of visits" yaxis: "Number of visits"
description: "Number of unique users who visited using a mobile device."
web_crawlers: web_crawlers:
title: "Web Crawler Requests" title: "Web Crawler Requests"
labels: labels:
user_agent: "User Agent" user_agent: "User Agent"
page_views: "Pageviews" page_views: "Pageviews"
description: "Number of new pageviews generated by all webcrawlers."
suspicious_logins: suspicious_logins:
title: "Suspicious Logins" title: "Suspicious Logins"
labels: labels:
@ -1135,12 +1163,14 @@ en:
device: Device device: Device
os: Operating System os: Operating System
login_time: Login Time login_time: Login Time
description: "Details of new logins that differ suspiciously from previous logins."
staff_logins: staff_logins:
title: "Staff Logins" title: "Staff Logins"
labels: labels:
user: User user: User
location: Location location: Location
login_at: Login at login_at: Login at
description: "List of admin and moderator login times with locations."
dashboard: dashboard:
rails_env_warning: "Your server is running in %{env} mode." rails_env_warning: "Your server is running in %{env} mode."

View File

@ -242,6 +242,7 @@ Discourse::Application.routes.draw do
get "dashboard/general" => "dashboard_next#general" get "dashboard/general" => "dashboard_next#general"
get "dashboard/moderation" => "dashboard_next#moderation" get "dashboard/moderation" => "dashboard_next#moderation"
get "dashboard/security" => "dashboard_next#security" get "dashboard/security" => "dashboard_next#security"
get "dashboard/reports" => "dashboard_next#reports"
get "dashboard-old" => "dashboard#index" get "dashboard-old" => "dashboard#index"

View File

@ -4,10 +4,28 @@ acceptance("Dashboard Next", {
loggedIn: true loggedIn: true
}); });
QUnit.test("Visit dashboard next page", async assert => { QUnit.test("Dashboard", async assert => {
await visit("/admin");
assert.ok(exists(".dashboard-next"), "has dashboard-next class");
});
QUnit.test("tabs", async assert => {
await visit("/admin"); await visit("/admin");
assert.ok(exists(".dashboard-next"), "has dashboard-next class"); assert.ok(exists(".dashboard-next .navigation-item.general"), "general tab");
assert.ok(
exists(".dashboard-next .navigation-item.moderation"),
"moderation tab"
);
assert.ok(
exists(".dashboard-next .navigation-item.security"),
"security tab"
);
assert.ok(exists(".dashboard-next .navigation-item.reports"), "reports tab");
});
QUnit.test("general tab", async assert => {
await visit("/admin");
assert.ok(exists(".admin-report.signups"), "signups report"); assert.ok(exists(".admin-report.signups"), "signups report");
assert.ok(exists(".admin-report.posts"), "posts report"); assert.ok(exists(".admin-report.posts"), "posts report");
@ -28,10 +46,6 @@ QUnit.test("Visit dashboard next page", async assert => {
"Houston...", "Houston...",
"displays problems" "displays problems"
); );
});
QUnit.test("it has counters", async assert => {
await visit("/admin");
assert.equal( assert.equal(
$(".admin-report.page-view-total-reqs .today-count") $(".admin-report.page-view-total-reqs .today-count")
@ -58,3 +72,37 @@ QUnit.test("it has counters", async assert => {
"80.8k" "80.8k"
); );
}); });
QUnit.test("reports tab", async assert => {
await visit("/admin");
await click(".dashboard-next .navigation-item.reports .navigation-link");
assert.equal(
find(".dashboard-next .reports-index.section .reports-list .report").length,
1
);
await fillIn(".dashboard-next .filter-reports-input", "flags");
assert.equal(
find(".dashboard-next .reports-index.section .reports-list .report").length,
0
);
await click(".dashboard-next .navigation-item.security .navigation-link");
await click(".dashboard-next .navigation-item.reports .navigation-link");
assert.equal(
find(".dashboard-next .reports-index.section .reports-list .report").length,
1,
"navigating back and forth resets filter"
);
await fillIn(".dashboard-next .filter-reports-input", "activities");
assert.equal(
find(".dashboard-next .reports-index.section .reports-list .report").length,
1,
"filter is case insensitive"
);
});