diff --git a/app/assets/javascripts/admin/controllers/admin-dashboard-next-reports.js.es6 b/app/assets/javascripts/admin/controllers/admin-dashboard-next-reports.js.es6 new file mode 100644 index 00000000000..9c2eccdab16 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-dashboard-next-reports.js.es6 @@ -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); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-dashboard-next-reports.js.es6 b/app/assets/javascripts/admin/routes/admin-dashboard-next-reports.js.es6 new file mode 100644 index 00000000000..7aea9010965 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-dashboard-next-reports.js.es6 @@ -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 }); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-reports-index.js.es6 b/app/assets/javascripts/admin/routes/admin-reports-index.js.es6 index da0bda906e0..240efaddda8 100644 --- a/app/assets/javascripts/admin/routes/admin-reports-index.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-reports-index.js.es6 @@ -1,13 +1,5 @@ -import { ajax } from "discourse/lib/ajax"; - export default Discourse.Route.extend({ - model() { - return ajax("/admin/reports").then(json => { - return json; - }); - }, - - setupController(controller, model) { - controller.setProperties({ model: model.reports }); + beforeModel() { + this.transitionTo("admin.dashboardNextReports"); } }); diff --git a/app/assets/javascripts/admin/routes/admin-route-map.js.es6 b/app/assets/javascripts/admin/routes/admin-route-map.js.es6 index 4ce0b8f3137..3ec2cc9c8cc 100644 --- a/app/assets/javascripts/admin/routes/admin-route-map.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-route-map.js.es6 @@ -12,6 +12,10 @@ export default function() { path: "/dashboard/security", resetNamespace: true }); + this.route("admin.dashboardNextReports", { + path: "/dashboard/reports", + resetNamespace: true + }); }); this.route( diff --git a/app/assets/javascripts/admin/templates/components/admin-report.hbs b/app/assets/javascripts/admin/templates/components/admin-report.hbs index 80f21c4a232..a7e76488bc2 100644 --- a/app/assets/javascripts/admin/templates/components/admin-report.hbs +++ b/app/assets/javascripts/admin/templates/components/admin-report.hbs @@ -6,7 +6,7 @@ {{outlet}} diff --git a/app/assets/javascripts/admin/templates/dashboard_next_general.hbs b/app/assets/javascripts/admin/templates/dashboard_next_general.hbs index 5842f31be7f..78970f7ae8e 100644 --- a/app/assets/javascripts/admin/templates/dashboard_next_general.hbs +++ b/app/assets/javascripts/admin/templates/dashboard_next_general.hbs @@ -5,7 +5,7 @@

- + {{i18n "admin.dashboard.community_health"}}

@@ -85,10 +85,6 @@ {{/each}}
- - {{#link-to "adminReports"}} - {{i18n "admin.dashboard.all_reports"}} - {{/link-to}}
diff --git a/app/assets/javascripts/admin/templates/dashboard_next_moderation.hbs b/app/assets/javascripts/admin/templates/dashboard_next_moderation.hbs index dbfbb02ea59..8959bac0219 100644 --- a/app/assets/javascripts/admin/templates/dashboard_next_moderation.hbs +++ b/app/assets/javascripts/admin/templates/dashboard_next_moderation.hbs @@ -4,7 +4,7 @@

- + {{i18n "admin.dashboard.moderators_activity"}}

diff --git a/app/assets/javascripts/admin/templates/dashboard_next_reports.hbs b/app/assets/javascripts/admin/templates/dashboard_next_reports.hbs new file mode 100644 index 00000000000..d6666cee713 --- /dev/null +++ b/app/assets/javascripts/admin/templates/dashboard_next_reports.hbs @@ -0,0 +1,28 @@ +{{#conditional-loading-spinner condition=isLoading}} +
+
+

{{i18n "admin.reports.title"}}

+ {{input + class="filter-reports-input" + input=(action "filterReports" value="target.value") + placeholder=(i18n "admin.dashboard.filter_reports") + autofocus=true}} +
+ +
    + {{#each filterReports as |report|}} +
  • + {{#link-to 'adminReports.show' report.type}} +

    {{report.title}}

    + + {{#if report.description}} +

    + {{report.description}} +

    + {{/if}} + {{/link-to}} +
  • + {{/each}} +
+
+{{/conditional-loading-spinner}} diff --git a/app/assets/javascripts/admin/templates/reports-index.hbs b/app/assets/javascripts/admin/templates/reports-index.hbs deleted file mode 100644 index 0c88b789587..00000000000 --- a/app/assets/javascripts/admin/templates/reports-index.hbs +++ /dev/null @@ -1,17 +0,0 @@ -

{{i18n "admin.reports.title"}}

- -
    - {{#each model as |report|}} -
  • - {{#link-to 'adminReports.show' report.type}} -

    {{report.title}}

    - {{/link-to}} - - {{#if report.description}} -

    - {{report.description}} -

    - {{/if}} -
  • - {{/each}} -
diff --git a/app/assets/stylesheets/common/admin/admin_reports.scss b/app/assets/stylesheets/common/admin/admin_reports.scss index 137bf814e57..3dbd7f4a011 100644 --- a/app/assets/stylesheets/common/admin/admin_reports.scss +++ b/app/assets/stylesheets/common/admin/admin_reports.scss @@ -13,20 +13,6 @@ height: 400px; } - .reports-list { - list-style: none; - margin: 0; - - .report { - padding-bottom: 0.5em; - margin-bottom: 0.5em; - - .report-description { - margin: 0; - } - } - } - .report-container { display: flex; diff --git a/app/assets/stylesheets/common/admin/dashboard_next.scss b/app/assets/stylesheets/common/admin/dashboard_next.scss index fcc81ec234c..fe8a5718d13 100644 --- a/app/assets/stylesheets/common/admin/dashboard_next.scss +++ b/app/assets/stylesheets/common/admin/dashboard_next.scss @@ -43,6 +43,10 @@ @include active-navigation-item; } + &.dashboard-next-reports .navigation-item.reports { + @include active-navigation-item; + } + &.general .navigation-item.general { @include active-navigation-item; } @@ -526,3 +530,33 @@ 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"); + } + } +} diff --git a/app/controllers/admin/dashboard_next_controller.rb b/app/controllers/admin/dashboard_next_controller.rb index aa0cebd3740..72e3a0cecd9 100644 --- a/app/controllers/admin/dashboard_next_controller.rb +++ b/app/controllers/admin/dashboard_next_controller.rb @@ -11,6 +11,7 @@ class Admin::DashboardNextController < Admin::AdminController def moderation; end def security; end + def reports; end def general render json: AdminDashboardNextGeneralData.fetch_cached_stats diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index c9c0c7d9f17..0a8f99e5f22 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2861,11 +2861,13 @@ en: general_tab: "General" moderation_tab: "Moderation" security_tab: "Security" + reports_tab: "Reports" disabled: Disabled timeout_error: Sorry, query is taking too long, please pick a shorter interval 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 reports: trend_title: "%{percent} change. Currently %{current}, was %{prev} in previous period." diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index a008d53ea36..ed2b431e7cf 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -900,6 +900,7 @@ en: editor: Editor author: Author edit_reason: Reason + description: "Number of new post edits." user_flagging_ratio: title: "User Flagging Ratio" labels: @@ -908,6 +909,7 @@ en: disagreed_flags: Disagreed flags ignored_flags: Ignored flags score: Score + description: "List of users ordered by ratio of staff response to their flags (disagreed to agreed)." moderators_activity: title: "Moderator Activity" labels: @@ -918,6 +920,7 @@ en: post_count: Posts created pm_count: PMs created revision_count: Revisions + description: List of moderator activity including flags reviewed, reading time, topics created, posts created, private messages created, and revisions. flags_status: title: "Flags Status" values: @@ -931,39 +934,42 @@ en: poster: Poster flagger: Flagger time_to_resolution: Resolution time + description: "List of flags' statuses including type of flag, poster, flagger, and time to resolution." visits: title: "User Visits" xaxis: "Day" yaxis: "Number of visits" + description: "Number of all unique user visits." signups: title: "Signups" xaxis: "Day" yaxis: "Number of signups" - description: "New account registrations for this period" + description: "New account registrations for this period." new_contributors: title: "New Contributors" xaxis: "Day" 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: title: "DAU/MAU" xaxis: "Day" 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: title: "Daily Engaged Users" xaxis: "Day" 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: title: "User Profile Views" xaxis: "Day" yaxis: "Number of user profiles viewed" + description: "Total new views of user profiles." topics: title: "Topics" xaxis: "Day" yaxis: "Number of new topics" - description: "New topics created during this period" + description: "New topics created during this period." posts: title: "Posts" xaxis: "Day" @@ -973,24 +979,24 @@ en: title: "Likes" xaxis: "Day" yaxis: "Number of new likes" + description: "Number of new likes." flags: title: "Flags" xaxis: "Day" yaxis: "Number of flags" + description: "Number of new flags." bookmarks: title: "Bookmarks" xaxis: "Day" yaxis: "Number of new bookmarks" - starred: - title: "Starred" - xaxis: "Day" - yaxis: "Number of new starred topics" + description: "Number of new topics and posts bookmarked." users_by_trust_level: title: "Users per Trust Level" xaxis: "Trust Level" yaxis: "Number of Users" labels: level: Level + description: "Number of users grouped by trust level." users_by_type: title: "Users per Type" xaxis: "Type" @@ -1002,40 +1008,49 @@ en: moderator: Moderator suspended: Suspended silenced: Silenced + description: "Number of users grouped by admin, moderator, suspended, and silenced." trending_search: title: Trending Search Terms labels: term: Term searches: Searches click_through: CTR + description: "Most popular search terms with their click-through rates." emails: title: "Emails Sent" xaxis: "Day" yaxis: "Number of Emails" + description: "Number of new emails sent." user_to_user_private_messages: title: "User-to-User (excluding replies)" xaxis: "Day" yaxis: "Number of messages" + description: "Number of newly initiated private messages." user_to_user_private_messages_with_replies: title: "User-to-User (with replies)" xaxis: "Day" yaxis: "Number of messages" + description: "Number of all new private messages and responses." system_private_messages: title: "System" xaxis: "Day" yaxis: "Number of messages" + description: "Number of private messages sent automatically by the System." moderator_warning_private_messages: title: "Moderator Warning" xaxis: "Day" yaxis: "Number of messages" + description: "Number of warnings sent by private messages from moderators." notify_moderators_private_messages: title: "Notify Moderators" xaxis: "Day" yaxis: "Number of messages" + description: "Number of times moderators have been privately notified by a flag." notify_user_private_messages: title: "Notify User" xaxis: "Day" yaxis: "Number of messages" + description: "Number of times users have been privately notified by a flag." top_referrers: title: "Top Referrers" xaxis: "User" @@ -1045,6 +1060,7 @@ en: user: "User" num_clicks: "Clicks" num_topics: "Topics" + description: "Users listed by number of clicks on links they have shared." top_traffic_sources: title: "Top Traffic Sources" xaxis: "Domain" @@ -1055,35 +1071,43 @@ en: domain: Domain num_clicks: Clicks num_topics: Topics + description: "External sources that have linked to this site the most." top_referred_topics: title: "Top Referred Topics" labels: num_clicks: "Clicks" topic: "Topic" + description: "Topics that have received the most clicks from external sources." page_view_anon_reqs: title: "Anonymous" xaxis: "Day" yaxis: "Anonymous Pageviews" + description: "Number of new page views by visitors not logged in to an account." page_view_logged_in_reqs: title: "Logged In" xaxis: "Day" yaxis: "Logged In Pageviews" + description: "Number of new pageviews from logged in users." page_view_crawler_reqs: title: "Web Crawlers" xaxis: "Day" yaxis: "Web Crawler Pageviews" + description: "Webcrawlers listed by number of new pageviews they have generated." page_view_total_reqs: title: "Pageviews" xaxis: "Day" yaxis: "Total Pageviews" + description: "Number of new pageviews from all visitors." page_view_logged_in_mobile_reqs: title: "Logged In Pageviews" xaxis: "Day" 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: title: "Anon Pageviews" xaxis: "Day" yaxis: "Mobile Anon Pageviews" + description: "Number of new pageviews from visitors on a mobile device who are not logged in." http_background_reqs: title: "Background" xaxis: "Day" @@ -1112,19 +1136,23 @@ en: title: "Time to first response" xaxis: "Day" yaxis: "Average time (hours)" + description: "Average time (in hours) of the first response to new topics." topics_with_no_response: title: "Topics with no response" xaxis: "Day" yaxis: "Total" + description: "Number of new topics created that did not receive a response." mobile_visits: title: "User Visits (mobile)" xaxis: "Day" yaxis: "Number of visits" + description: "Number of unique users who visited using a mobile device." web_crawlers: title: "Web Crawler Requests" labels: user_agent: "User Agent" page_views: "Pageviews" + description: "Number of new pageviews generated by all webcrawlers." suspicious_logins: title: "Suspicious Logins" labels: @@ -1135,12 +1163,14 @@ en: device: Device os: Operating System login_time: Login Time + description: "Details of new logins that differ suspiciously from previous logins." staff_logins: title: "Staff Logins" labels: user: User location: Location login_at: Login at + description: "List of admin and moderator login times with locations." dashboard: rails_env_warning: "Your server is running in %{env} mode." diff --git a/config/routes.rb b/config/routes.rb index a99df2769c3..74c24af19bb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -242,6 +242,7 @@ Discourse::Application.routes.draw do get "dashboard/general" => "dashboard_next#general" get "dashboard/moderation" => "dashboard_next#moderation" get "dashboard/security" => "dashboard_next#security" + get "dashboard/reports" => "dashboard_next#reports" get "dashboard-old" => "dashboard#index" diff --git a/test/javascripts/acceptance/dashboard-next-test.js.es6 b/test/javascripts/acceptance/dashboard-next-test.js.es6 index d142e32d720..0f550953a8a 100644 --- a/test/javascripts/acceptance/dashboard-next-test.js.es6 +++ b/test/javascripts/acceptance/dashboard-next-test.js.es6 @@ -4,10 +4,28 @@ acceptance("Dashboard Next", { 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"); - 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.posts"), "posts report"); @@ -28,10 +46,6 @@ QUnit.test("Visit dashboard next page", async assert => { "Houston...", "displays problems" ); -}); - -QUnit.test("it has counters", async assert => { - await visit("/admin"); assert.equal( $(".admin-report.page-view-total-reqs .today-count") @@ -58,3 +72,37 @@ QUnit.test("it has counters", async assert => { "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" + ); +});