diff --git a/app/assets/javascripts/admin/components/dashboard-inline-table.js.es6 b/app/assets/javascripts/admin/components/dashboard-inline-table.js.es6 index 7a1720db3e7..c37863e76d0 100644 --- a/app/assets/javascripts/admin/components/dashboard-inline-table.js.es6 +++ b/app/assets/javascripts/admin/components/dashboard-inline-table.js.es6 @@ -1,21 +1,22 @@ -import { ajax } from 'discourse/lib/ajax'; +import { ajax } from "discourse/lib/ajax"; import Report from "admin/models/report"; import AsyncReport from "admin/mixins/async-report"; export default Ember.Component.extend(AsyncReport, { classNames: ["dashboard-table", "dashboard-inline-table", "fixed"], - isLoading: true, help: null, helpPage: null, + title: null, + loadingTitle: null, loadReport(report_json) { - this._setPropertiesFromReport(Report.create(report_json)); + return Report.create(report_json); }, fetchReport() { - this.set("isLoading", true); + this._super(); - let payload = { data: { async: true } }; + let payload = { data: { async: true, facets: ["total", "prev30Days"] } }; if (this.get("startDate")) { payload.data.start_date = this.get("startDate").format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ"); @@ -29,14 +30,15 @@ export default Ember.Component.extend(AsyncReport, { payload.data.limit = this.get("limit"); } - ajax(this.get("dataSource"), payload) - .then((response) => { - this.set('reportKey', response.report.report_key); - this.loadReport(response.report); - }).finally(() => { - if (!Ember.isEmpty(this.get("report.data"))) { - this.set("isLoading", false); - }; - }); + this.set("reports", Ember.Object.create()); + this.set("reportKeys", []); + + return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => { + return ajax(dataSource, payload) + .then(response => { + this.set(`reports.${response.report.report_key}`, this.loadReport(response.report)); + this.get("reportKeys").pushObject(response.report.report_key); + }); + })); } }); diff --git a/app/assets/javascripts/admin/components/dashboard-mini-chart.js.es6 b/app/assets/javascripts/admin/components/dashboard-mini-chart.js.es6 index 9839e3296fb..866e287b4b5 100644 --- a/app/assets/javascripts/admin/components/dashboard-mini-chart.js.es6 +++ b/app/assets/javascripts/admin/components/dashboard-mini-chart.js.es6 @@ -1,5 +1,4 @@ import { ajax } from "discourse/lib/ajax"; -import computed from "ember-addons/ember-computed-decorators"; import AsyncReport from "admin/mixins/async-report"; import Report from "admin/models/report"; import { number } from 'discourse/lib/formatter'; @@ -26,41 +25,20 @@ function collapseWeekly(data, average) { export default Ember.Component.extend(AsyncReport, { classNames: ["dashboard-mini-chart"], - classNameBindings: ["trend", "oneDataPoint"], - isLoading: true, - trend: Ember.computed.alias("report.trend"), - oneDataPoint: false, - backgroundColor: "rgba(200,220,240,0.3)", - borderColor: "#08C", - average: false, - percent: false, total: 0, - @computed("dataSourceName") - dataSource(dataSourceName) { - if (dataSourceName) { - return `/admin/reports/${dataSourceName}`; - } + init() { + this._super(); + + this._colorsPool = ["rgb(0,136,204)", "rgb(235,83,148)"]; }, - @computed("trend") - trendIcon(trend) { - switch (trend) { - case "trending-up": - return "angle-up"; - case "trending-down": - return "angle-down"; - case "high-trending-up": - return "angle-double-up"; - case "high-trending-down": - return "angle-double-down"; - default: - return null; - } + pickColorAtIndex(index) { + return this._colorsPool[index] || this._colorsPool[0]; }, fetchReport() { - this.set("isLoading", true); + this._super(); let payload = { data: { async: true, facets: ["prev_period"] } @@ -79,56 +57,56 @@ export default Ember.Component.extend(AsyncReport, { this._chart = null; } - this.set("report", null); + this.set("reports", Ember.Object.create()); + this.set("reportKeys", []); - ajax(this.get("dataSource"), payload) - .then((response) => { - this.set('reportKey', response.report.report_key); - this.loadReport(response.report); - }) - .finally(() => { - if (this.get("oneDataPoint")) { - this.set("isLoading", false); - return; - } - - if (!Ember.isEmpty(this.get("report.data"))) { - this.set("isLoading", false); - this.renderReport(); - } - }); + return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => { + return ajax(dataSource, payload) + .then(response => { + this.set(`reports.${response.report.report_key}`, this.loadReport(response.report)); + this.get("reportKeys").pushObject(response.report.report_key); + }); + })); }, - loadReport(report) { - if (_.isArray(report.data)) { - Report.fillMissingDates(report); + loadReport(report, previousReport) { + Report.fillMissingDates(report); - if (report.data && report.data.length > 40) { - report.data = collapseWeekly(report.data, this.get("average")); - } - - const model = Report.create(report); - this._setPropertiesFromReport(model); + if (report.data && report.data.length > 40) { + report.data = collapseWeekly(report.data, report.average); } + + if (previousReport && previousReport.color.length) { + report.color = previousReport.color; + } else { + const dataSourceNameIndex = this.get("dataSourceNames").split(",").indexOf(report.type); + report.color = this.pickColorAtIndex(dataSourceNameIndex); + } + + return Report.create(report); }, renderReport() { - if (!this.element || this.isDestroying || this.isDestroyed) { return; } - if (this.get("oneDataPoint")) return; + this._super(); Ember.run.schedule("afterRender", () => { const $chartCanvas = this.$(".chart-canvas"); - if (!$chartCanvas.length) return; const context = $chartCanvas[0].getContext("2d"); + const reports = _.values(this.get("reports")); + + const labels = Ember.makeArray(reports.get("firstObject.data")).map(d => d.x); + const data = { - labels: this.get("labels"), - datasets: [{ - data: Ember.makeArray(this.get("values")), - backgroundColor: this.get("backgroundColor"), - borderColor: this.get("borderColor") - }] + labels, + datasets: reports.map(report => { + return { + data: Ember.makeArray(report.data).map(d => d.y), + backgroundColor: "rgba(200,220,240,0.3)", + borderColor: report.color + }; + }) }; if (this._chart) { @@ -138,15 +116,6 @@ export default Ember.Component.extend(AsyncReport, { }); }, - _setPropertiesFromReport(report) { - const oneDataPoint = (this.get("startDate") && this.get("endDate")) && - this.get("startDate").isSame(this.get("endDate"), "day"); - - report.set("average", this.get("average")); - report.set("percent", this.get("percent")); - this.setProperties({ oneDataPoint, report }); - }, - _buildChartConfig(data) { return { type: "line", @@ -171,6 +140,7 @@ export default Ember.Component.extend(AsyncReport, { }], xAxes: [{ display: true, + gridLines: { display: false }, type: "time", time: { parser: "YYYY-MM-DD" diff --git a/app/assets/javascripts/admin/mixins/async-report.js.es6 b/app/assets/javascripts/admin/mixins/async-report.js.es6 index 12b267b059e..352c0cb4359 100644 --- a/app/assets/javascripts/admin/mixins/async-report.js.es6 +++ b/app/assets/javascripts/admin/mixins/async-report.js.es6 @@ -1,77 +1,101 @@ -import computed from 'ember-addons/ember-computed-decorators'; +import computed from "ember-addons/ember-computed-decorators"; export default Ember.Mixin.create({ classNameBindings: ["isLoading"], - report: null, + reports: null, + reportKeys: null, + isLoading: false, + dataSourceNames: "", init() { this._super(); - this._channel = this.get("dataSource"); + this.set("reports", Ember.Object.create()); + this.set("reportKeys", []); + + this._channels = this.get("dataSources"); this._callback = (report) => { - if (report.report_key = this.get("reportKey")) { + if (this.get("reportKeys").includes(report.report_key)) { Em.run.next(() => { - if (report.report_key = this.get("reportKey")) { - this.loadReport(report); - this.set("isLoading", false); + if (this.get("reportKeys").includes(report.report_key)) { + const previousReport = this.get(`reports.${report.report_key}`); + this.set(`reports.${report.report_key}`, this.loadReport(report, previousReport)); this.renderReport(); } }); } }; + // in case we did not subscribe in time ensure we always grab the // last thing on the channel - this.messageBus.subscribe(this._channel, this._callback, -2); + this.subscribe(-2); + }, + + subscribe(position) { + this._channels.forEach(channel => { + this.messageBus.subscribe(channel, this._callback, position); + }); + }, + + unsubscribe() { + this._channels.forEach(channel => { + this.messageBus.unsubscribe(channel, this._callback); + }); + }, + + @computed("dataSourceNames") + dataSources(dataSourceNames) { + return dataSourceNames.split(",").map(source => `/admin/reports/${source}`); }, willDestroyElement() { this._super(); - this.messageBus.unsubscribe(this._channel, this._callback); + + this.unsubscribe(); }, didInsertElement() { this._super(); Ember.run.later(this, function() { - this.fetchReport(); + this.fetchReport() + .finally(() => { + this.renderReport(); + }); }, 500); }, didUpdateAttrs() { this._super(); - this.fetchReport(); + this.fetchReport() + .finally(() => { + this.renderReport(); + }); }, - renderReport() {}, + renderReport() { + if (!this.element || this.isDestroying || this.isDestroyed) return; + + const reports = _.values(this.get("reports")); + + if (!reports.length) return; + + const title = reports.map(report => report.title).join(", "); + + if (reports.map(report => report.processing).includes(true)) { + const loading = I18n.t("conditional_loading_section.loading"); + this.set("loadingTitle", `${loading}\n\n${title}`); + return; + } + + this.setProperties({ title, isLoading: false}); + }, loadReport() {}, - fetchReport() {}, - - @computed("dataSourceName") - dataSource(dataSourceName) { - return `/admin/reports/${dataSourceName}`; + fetchReport() { + this.set("isLoading", true); + this.set("loadingTitle", I18n.t("conditional_loading_section.loading")); }, - - @computed("report") - labels(report) { - if (!report) return; - if (report.labels) { - return Ember.makeArray(report.labels); - } else { - return Ember.makeArray(report.data).map(r => r.x); - } - }, - - @computed("report") - values(report) { - if (!report) return; - return Ember.makeArray(report.data).map(r => r.y); - }, - - _setPropertiesFromReport(report) { - if (!this.element || this.isDestroying || this.isDestroyed) { return; } - this.setProperties({ report }); - } }); diff --git a/app/assets/javascripts/admin/models/report.js.es6 b/app/assets/javascripts/admin/models/report.js.es6 index 853457570d6..94e72eaac47 100644 --- a/app/assets/javascripts/admin/models/report.js.es6 +++ b/app/assets/javascripts/admin/models/report.js.es6 @@ -5,6 +5,7 @@ import computed from 'ember-addons/ember-computed-decorators'; const Report = Discourse.Model.extend({ average: false, + percent: false, @computed("type", "start_date", "end_date") reportUrl(type, start_date, end_date) { @@ -101,7 +102,23 @@ const Report = Discourse.Model.extend({ @computed('data', 'currentTotal') currentAverage(data, total) { - return data.length === 0 ? 0 : parseFloat((total / parseFloat(data.length)).toFixed(1)); + return Ember.makeArray(data).length === 0 ? 0 : parseFloat((total / parseFloat(data.length)).toFixed(1)); + }, + + @computed("trend") + trendIcon(trend) { + switch (trend) { + case "trending-up": + return "angle-up"; + case "trending-down": + return "angle-down"; + case "high-trending-up": + return "angle-double-up"; + case "high-trending-down": + return "angle-double-down"; + default: + return null; + } }, @computed('prev_period', 'currentTotal', 'currentAverage') diff --git a/app/assets/javascripts/admin/templates/components/dashboard-inline-table.hbs b/app/assets/javascripts/admin/templates/components/dashboard-inline-table.hbs index ce075761a5d..90cba2318b5 100644 --- a/app/assets/javascripts/admin/templates/components/dashboard-inline-table.hbs +++ b/app/assets/javascripts/admin/templates/components/dashboard-inline-table.hbs @@ -1,32 +1,40 @@ -{{#conditional-loading-section isLoading=isLoading title=report.title}} +{{#conditional-loading-section isLoading=isLoading title=loadingTitle}}
-| {{label}} | - {{/each}} -
|---|
| {{number value}} | -
| {{label}} | + {{/each}} + {{else}} + {{#each report.data as |data|}} +{{data.x}} | + {{/each}} + {{/if}} +
|---|---|
| {{number data.y}} | +
{{diskSpace.backups_used}} ({{i18n "admin.dashboard.space_free" size=diskSpace.backups_free}})
@@ -125,7 +106,7 @@
{{/if}}
{{diskSpace.uploads_used}} ({{i18n "admin.dashboard.space_free" size=diskSpace.uploads_free}})
@@ -153,7 +134,7 @@