UX: switch dashboard to be the new dashboard

Also:
- add pageviews
- add problems and version sections
This commit is contained in:
Sam 2018-05-14 13:07:59 +10:00
parent d6316ac4b9
commit 6332d5040d
15 changed files with 137 additions and 114 deletions

View File

@ -1,7 +1,11 @@
import { setting } from 'discourse/lib/computed';
import DiscourseURL from "discourse/lib/url";
import computed from "ember-addons/ember-computed-decorators";
import AdminDashboardNext from "admin/models/admin-dashboard-next";
import Report from "admin/models/report";
import VersionCheck from 'admin/models/version-check';
const PROBLEMS_CHECK_MINUTES = 1;
export default Ember.Controller.extend({
queryParams: ["period"],
@ -9,10 +13,28 @@ export default Ember.Controller.extend({
isLoading: false,
dashboardFetchedAt: null,
exceptionController: Ember.inject.controller("exception"),
showVersionChecks: setting('version_checks'),
diskSpace: Ember.computed.alias("model.attributes.disk_space"),
availablePeriods: ["yearly", "quarterly", "monthly", "weekly"],
@computed('problems.length')
foundProblems(problemsLength) {
return this.currentUser.get('admin') && (problemsLength || 0) > 0;
},
@computed('foundProblems')
thereWereProblems(foundProblems) {
if (!this.currentUser.get('admin')) { return false; }
if (foundProblems) {
this.set('hadProblems', true);
return true;
} else {
return this.get('hadProblems') || false;
}
},
fetchDashboard() {
if (this.get("isLoading")) return;
@ -20,7 +42,14 @@ export default Ember.Controller.extend({
if (!this.get("dashboardFetchedAt") || moment().subtract(30, "minutes").toDate() > this.get("dashboardFetchedAt")) {
this.set("isLoading", true);
const versionChecks = this.siteSettings.version_checks;
AdminDashboardNext.find().then(adminDashboardNextModel => {
if (versionChecks) {
this.set('versionCheck', VersionCheck.create(adminDashboardNextModel.version_check));
}
this.setProperties({
dashboardFetchedAt: new Date(),
model: adminDashboardNextModel,
@ -33,6 +62,25 @@ export default Ember.Controller.extend({
this.set("isLoading", false);
});
}
if (!this.get('problemsFetchedAt') || moment().subtract(PROBLEMS_CHECK_MINUTES, 'minutes').toDate() > this.get('problemsFetchedAt')) {
this.loadProblems();
}
},
loadProblems() {
this.set('loadingProblems', true);
this.set('problemsFetchedAt', new Date());
AdminDashboardNext.fetchProblems().then(d => {
this.set('problems', d.problems);
}).finally(() => {
this.set('loadingProblems', false);
});
},
@computed('problemsFetchedAt')
problemsTimestamp(problemsFetchedAt) {
return moment(problemsFetchedAt).format('LLL');
},
@computed("period")
@ -80,7 +128,10 @@ export default Ember.Controller.extend({
actions: {
changePeriod(period) {
DiscourseURL.routeTo(this._reportsForPeriodURL(period));
}
},
refreshProblems() {
this.loadProblems();
},
},
_reportsForPeriodURL(period) {

View File

@ -1,11 +1,8 @@
import { setting } from 'discourse/lib/computed';
import AdminDashboard from 'admin/models/admin-dashboard';
import VersionCheck from 'admin/models/version-check';
import Report from 'admin/models/report';
import AdminUser from 'admin/models/admin-user';
import computed from 'ember-addons/ember-computed-decorators';
const PROBLEMS_CHECK_MINUTES = 1;
const ATTRIBUTES = [ 'disk_space','admins', 'moderators', 'silenced', 'suspended', 'top_traffic_sources',
'top_referred_topics', 'updated_at'];
@ -18,35 +15,13 @@ export default Ember.Controller.extend({
loading: null,
versionCheck: null,
dashboardFetchedAt: null,
showVersionChecks: setting('version_checks'),
exceptionController: Ember.inject.controller('exception'),
@computed('problems.length')
foundProblems(problemsLength) {
return this.currentUser.get('admin') && (problemsLength || 0) > 0;
},
@computed('foundProblems')
thereWereProblems(foundProblems) {
if (!this.currentUser.get('admin')) { return false; }
if (foundProblems) {
this.set('hadProblems', true);
return true;
} else {
return this.get('hadProblems') || false;
}
},
fetchDashboard() {
if (!this.get('dashboardFetchedAt') || moment().subtract(30, 'minutes').toDate() > this.get('dashboardFetchedAt')) {
this.set('loading', true);
const versionChecks = this.siteSettings.version_checks;
AdminDashboard.find().then(d => {
this.set('dashboardFetchedAt', new Date());
if (versionChecks) {
this.set('versionCheck', VersionCheck.create(d.version_check));
}
REPORTS.forEach(name => this.set(name, d[name].map(r => Report.create(r))));
@ -64,26 +39,8 @@ export default Ember.Controller.extend({
this.set('loading', false);
});
}
if (!this.get('problemsFetchedAt') || moment().subtract(PROBLEMS_CHECK_MINUTES, 'minutes').toDate() > this.get('problemsFetchedAt')) {
this.loadProblems();
}
},
loadProblems() {
this.set('loadingProblems', true);
this.set('problemsFetchedAt', new Date());
AdminDashboard.fetchProblems().then(d => {
this.set('problems', d.problems);
}).finally(() => {
this.set('loadingProblems', false);
});
},
@computed('problemsFetchedAt')
problemsTimestamp(problemsFetchedAt) {
return moment(problemsFetchedAt).format('LLL');
},
@computed('updated_at')
updatedTimestamp(updatedAt) {
@ -91,9 +48,6 @@ export default Ember.Controller.extend({
},
actions: {
refreshProblems() {
this.loadProblems();
},
showTrafficReport() {
this.set("showTrafficReport", true);
}

View File

@ -13,10 +13,13 @@ AdminDashboardNext.reopenClass({
@return {jqXHR} a jQuery Promise object
**/
find() {
return ajax("/admin/dashboard-next.json").then(function(json) {
var model = AdminDashboardNext.create();
model.set("reports", json.reports);
model.set("version_check", json.version_check);
const attributes = {};
ATTRIBUTES.forEach(a => attributes[a] = json[a]);
@ -24,6 +27,25 @@ AdminDashboardNext.reopenClass({
model.set("loaded", true);
return model;
});
},
/**
Only fetch the list of problems that should be rendered on the dashboard.
The model will only have its "problems" attribute set.
@method fetchProblems
@return {jqXHR} a jQuery Promise object
**/
fetchProblems: function() {
return ajax("/admin/dashboard/problems.json", {
type: 'GET',
dataType: 'json'
}).then(function(json) {
var model = AdminDashboardNext.create(json);
model.set('loaded', true);
return model;
});
}

View File

@ -19,23 +19,6 @@ AdminDashboard.reopenClass({
});
},
/**
Only fetch the list of problems that should be rendered on the dashboard.
The model will only have its "problems" attribute set.
@method fetchProblems
@return {jqXHR} a jQuery Promise object
**/
fetchProblems: function() {
return ajax("/admin/dashboard/problems.json", {
type: 'GET',
dataType: 'json'
}).then(function(json) {
var model = AdminDashboard.create(json);
model.set('loaded', true);
return model;
});
}
});
export default AdminDashboard;

View File

@ -1,7 +1,7 @@
export default function() {
this.route('admin', { resetNamespace: true }, function() {
this.route('dashboard', { path: '/' });
this.route('dashboardNext', { path: '/dashboard-next' });
this.route('dashboard', { path: '/dashboard-old' });
this.route('dashboardNext', { path: '/' });
this.route('adminSiteSettings', { path: '/site_settings', resetNamespace: true }, function() {
this.route('adminSiteSettingsCategory', { path: 'category/:category_id', resetNamespace: true} );
});

View File

@ -3,7 +3,7 @@
<div class="full-width">
<ul class="nav nav-pills">
{{nav-item route='admin.dashboard' label='admin.dashboard.title'}}
{{nav-item route='admin.dashboardNext' label='admin.dashboard.title'}}
{{#if currentUser.admin}}
{{nav-item route='adminSiteSettings' label='admin.site_settings.title'}}
{{/if}}

View File

@ -0,0 +1,35 @@
{{#if foundProblems}}
<div class="dashboard-stats detected-problems">
<div class="look-here">{{d-icon "exclamation-triangle"}}</div>
<div class="problem-messages">
{{#conditional-loading-spinner condition=loadingProblems}}
<p>
{{i18n 'admin.dashboard.problems_found'}}
<ul class="{{if loadingProblems 'invisible'}}">
{{#each problems as |problem|}}
<li>{{{problem}}}</li>
{{/each}}
</ul>
</p>
<p class="actions">
<small>{{i18n 'admin.dashboard.last_checked'}}: {{problemsTimestamp}}</small>
{{d-button action="refreshProblems" class="btn-small" icon="refresh" label="admin.dashboard.refresh_problems"}}
</p>
{{/conditional-loading-spinner}}
</div>
<div class="clearfix"></div>
</div>
{{else}}
{{#if thereWereProblems}}
<div class="dashboard-stats detected-problems">
<div class="look-here">&nbsp;</div>
<div class="problem-messages">
<p>
{{i18n 'admin.dashboard.no_problems'}}
{{d-button action="refreshProblems" class="btn-small" icon="refresh" label="admin.dashboard.refresh_problems"}}
</p>
</div>
<div class="clearfix"></div>
</div>
{{/if}}
{{/if}}

View File

@ -1,11 +1,5 @@
{{plugin-outlet name="admin-dashboard-top"}}
{{#conditional-loading-spinner condition=loading}}
<div class="dashboard-left">
{{#if showVersionChecks}}
{{partial 'admin/templates/version-checks'}}
{{/if}}
<div class="dashboard-stats trust-levels">
<table class="table table-condensed table-hover">
<thead>
@ -174,42 +168,6 @@
</div>
<div class="dashboard-right">
{{#if foundProblems}}
<div class="dashboard-stats detected-problems">
<div class="look-here">{{d-icon "exclamation-triangle"}}</div>
<div class="problem-messages">
{{#conditional-loading-spinner condition=loadingProblems}}
<p>
{{i18n 'admin.dashboard.problems_found'}}
<ul class="{{if loadingProblems 'invisible'}}">
{{#each problems as |problem|}}
<li>{{{problem}}}</li>
{{/each}}
</ul>
</p>
<p class="actions">
<small>{{i18n 'admin.dashboard.last_checked'}}: {{problemsTimestamp}}</small>
{{d-button action="refreshProblems" class="btn-small" icon="refresh" label="admin.dashboard.refresh_problems"}}
</p>
{{/conditional-loading-spinner}}
</div>
<div class="clearfix"></div>
</div>
{{else}}
{{#if thereWereProblems}}
<div class="dashboard-stats detected-problems">
<div class="look-here">&nbsp;</div>
<div class="problem-messages">
<p>
{{i18n 'admin.dashboard.no_problems'}}
{{d-button action="refreshProblems" class="btn-small" icon="refresh" label="admin.dashboard.refresh_problems"}}
</p>
</div>
<div class="clearfix"></div>
</div>
{{/if}}
{{/if}}
<div class="dashboard-stats">
<table class="table table-condensed table-hover">
<thead>

View File

@ -1,4 +1,16 @@
{{plugin-outlet name="admin-dashboard-top"}}
{{plugin-outlet name="admin-dashboard-top"}}
<div class="section-top">
{{#if showVersionChecks}}
<div class="dashboard-left">
{{partial 'admin/templates/version-checks'}}
</div>
<div class="dashboard-right">
{{partial 'admin/templates/dashboard-problems'}}
</div>
{{/if}}
<div class='clearfix'></div>
</div>
<div class="community-health section">
<div class="section-title">
@ -105,6 +117,12 @@
<hr />
<p>
{{i18n 'admin.dashboard.find_old'}} {{#link-to 'admin.dashboard'}}{{i18n "admin.dashboard.old_link"}}{{/link-to}}
</p>
<hr />
<p class="last-dashboard-update">
{{i18n "admin.dashboard.last_updated"}} {{updatedTimestamp}}
</p>

View File

@ -3,7 +3,6 @@ class Admin::DashboardController < Admin::AdminController
def index
dashboard_data = AdminDashboardData.fetch_cached_stats || Jobs::DashboardStats.new.execute({})
dashboard_data.merge!(version_check: DiscourseUpdates.check_version.as_json) if SiteSetting.version_checks?
dashboard_data[:disk_space] = DiskSpace.cached_stats
render json: dashboard_data
end

View File

@ -3,6 +3,7 @@ require 'disk_space'
class Admin::DashboardNextController < Admin::AdminController
def index
dashboard_data = AdminDashboardNextData.fetch_cached_stats
dashboard_data.merge!(version_check: DiscourseUpdates.check_version.as_json) if SiteSetting.version_checks?
dashboard_data[:disk_space] = DiskSpace.cached_stats
render json: dashboard_data
end

View File

@ -15,8 +15,8 @@ module Jobs
end
# TODO: decide if we want to keep caching this every 30 minutes
AdminDashboardData.refresh_stats
AdminDashboardNextData.refresh_stats
AdminDashboardData.refresh_stats
end
end
end

View File

@ -1,7 +1,7 @@
class AdminDashboardNextData
include StatsCacheable
REPORTS = [ "visits", "posts", "time_to_first_response", "likes", "flags" ]
REPORTS = ["page_view_total_reqs", "visits", "posts", "time_to_first_response", "likes", "flags" ]
def initialize(opts = {})
@opts = opts

View File

@ -2713,6 +2713,8 @@ en:
dashboard:
title: "Dashboard"
last_updated: "Dashboard last updated:"
find_old: "Looking for the old dashboard?"
old_link: "visit it here"
version: "Version"
up_to_date: "You're up to date!"
critical_available: "A critical update is available."

View File

@ -972,7 +972,7 @@ en:
xaxis: "Day"
yaxis: "Web Crawler Pageviews"
page_view_total_reqs:
title: "Total"
title: "Pageviews"
xaxis: "Day"
yaxis: "Total Pageviews"
page_view_logged_in_mobile_reqs: