FEATURE: allows to display charts by day/week/month (#10325)

This commit is contained in:
Joffrey JAFFEUX 2020-07-28 16:14:41 +02:00 committed by GitHub
parent bc11769118
commit 0c7eaa57b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 148 additions and 8 deletions

View File

@ -8,6 +8,7 @@ export default Component.extend({
classNames: ["admin-report-chart"], classNames: ["admin-report-chart"],
limit: 8, limit: 8,
total: 0, total: 0,
options: null,
init() { init() {
this._super(...arguments); this._super(...arguments);
@ -49,7 +50,11 @@ export default Component.extend({
if (!chartCanvas) return; if (!chartCanvas) return;
const context = chartCanvas.getContext("2d"); const context = chartCanvas.getContext("2d");
const chartData = makeArray(model.get("chartData") || model.get("data")); const chartData = this._applyChartGrouping(
model,
makeArray(model.get("chartData") || model.get("data"), "weekly"),
this.options
);
const prevChartData = makeArray( const prevChartData = makeArray(
model.get("prevChartData") || model.get("prev_data") model.get("prevChartData") || model.get("prev_data")
); );
@ -91,11 +96,14 @@ export default Component.extend({
return; return;
} }
this._chart = new window.Chart(context, this._buildChartConfig(data)); this._chart = new window.Chart(
context,
this._buildChartConfig(data, this.options)
);
}); });
}, },
_buildChartConfig(data) { _buildChartConfig(data, options) {
return { return {
type: "line", type: "line",
data, data,
@ -144,7 +152,7 @@ export default Component.extend({
gridLines: { display: false }, gridLines: { display: false },
type: "time", type: "time",
time: { time: {
parser: "YYYY-MM-DD" unit: this._unitForGrouping(options)
}, },
ticks: { ticks: {
sampleSize: 5, sampleSize: 5,
@ -163,5 +171,65 @@ export default Component.extend({
this._chart.destroy(); this._chart.destroy();
this._chart = null; this._chart = null;
} }
},
_applyChartGrouping(model, data, options) {
if (!options.chartGrouping || options.chartGrouping === "daily") {
return data;
}
if (
options.chartGrouping === "weekly" ||
options.chartGrouping === "monthly"
) {
const isoKind = options.chartGrouping === "weekly" ? "isoWeek" : "month";
const kind = options.chartGrouping === "weekly" ? "week" : "month";
const startMoment = moment(model.start_date, "YYYY-MM-DD");
let currentIndex = 0;
let currentStart = startMoment.clone().startOf(isoKind);
let currentEnd = startMoment.clone().endOf(isoKind);
const transformedData = [
{
x: currentStart.format("YYYY-MM-DD"),
y: 0
}
];
data.forEach(d => {
let date = moment(d.x, "YYYY-MM-DD");
if (!date.isBetween(currentStart, currentEnd)) {
currentIndex += 1;
currentStart = currentStart.add(1, kind).startOf(isoKind);
currentEnd = currentEnd.add(1, kind).endOf(isoKind);
}
if (transformedData[currentIndex]) {
transformedData[currentIndex].y += d.y;
} else {
transformedData[currentIndex] = {
x: d.x,
y: d.y
};
}
});
return transformedData;
}
// ensure we return something if grouping is unknown
return data;
},
_unitForGrouping(options) {
switch (options.chartGrouping) {
case "monthly":
return "month";
case "weekly":
return "week";
default:
return "day";
}
} }
}); });

View File

@ -131,6 +131,18 @@ export default Component.extend({
return displayedModesLength > 1; return displayedModesLength > 1;
}, },
@discourseComputed("currentMode")
isChartMode(currentMode) {
return currentMode === "chart";
},
@action
changeGrouping(grouping) {
this.send("refreshReport", {
chartGrouping: grouping
});
},
@discourseComputed("currentMode", "model.modes", "forcedModes") @discourseComputed("currentMode", "model.modes", "forcedModes")
displayedModes(currentMode, reportModes, forcedModes) { displayedModes(currentMode, reportModes, forcedModes) {
const modes = forcedModes ? forcedModes.split(",") : reportModes; const modes = forcedModes ? forcedModes.split(",") : reportModes;
@ -184,6 +196,19 @@ export default Component.extend({
return reportKey; return reportKey;
}, },
@discourseComputed("reportOptions.chartGrouping")
chartGroupings(chartGrouping) {
chartGrouping = chartGrouping || "daily";
return ["daily", "weekly", "monthly"].map(id => {
return {
id,
label: `admin.dashboard.reports.${id}`,
class: `chart-grouping ${chartGrouping === id ? "active" : "inactive"}`
};
});
},
@action @action
onChangeDateRange(range) { onChangeDateRange(range) {
this.send("refreshReport", { this.send("refreshReport", {
@ -211,6 +236,7 @@ export default Component.extend({
refreshReport(options = {}) { refreshReport(options = {}) {
this.attrs.onRefresh({ this.attrs.onRefresh({
type: this.get("model.type"), type: this.get("model.type"),
chartGrouping: options.chartGrouping,
startDate: startDate:
typeof options.startDate === "undefined" typeof options.startDate === "undefined"
? this.startDate ? this.startDate
@ -243,6 +269,10 @@ export default Component.extend({
@action @action
changeMode(mode) { changeMode(mode) {
this.set("currentMode", mode); this.set("currentMode", mode);
this.send("refreshReport", {
chartGrouping: null
});
}, },
_computeReport() { _computeReport() {
@ -364,7 +394,9 @@ export default Component.extend({
} else { } else {
const chartOptions = JSON.parse(JSON.stringify(CHART_OPTIONS)); const chartOptions = JSON.parse(JSON.stringify(CHART_OPTIONS));
return EmberObject.create( return EmberObject.create(
Object.assign(chartOptions, this.get("reportOptions.chart") || {}) Object.assign(chartOptions, this.get("reportOptions.chart") || {}, {
chartGrouping: this.get("reportOptions.chartGrouping")
})
); );
} }
}, },

View File

@ -2,10 +2,11 @@ import discourseComputed from "discourse-common/utils/decorators";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
export default Controller.extend({ export default Controller.extend({
queryParams: ["start_date", "end_date", "filters"], queryParams: ["start_date", "end_date", "filters", "chart_grouping"],
start_date: null, start_date: null,
end_date: null, end_date: null,
filters: null, filters: null,
chart_grouping: null,
@discourseComputed("model.type") @discourseComputed("model.type")
reportOptions(type) { reportOptions(type) {
@ -15,6 +16,8 @@ export default Controller.extend({
options.table.limit = 10; options.table.limit = 10;
} }
options.chartGrouping = this.chart_grouping;
return options; return options;
} }
}); });

View File

@ -4,7 +4,8 @@ export default DiscourseRoute.extend({
queryParams: { queryParams: {
start_date: { refreshModel: true }, start_date: { refreshModel: true },
end_date: { refreshModel: true }, end_date: { refreshModel: true },
filters: { refreshModel: true } filters: { refreshModel: true },
chart_grouping: { refreshModel: true }
}, },
model(params) { model(params) {
@ -27,6 +28,9 @@ export default DiscourseRoute.extend({
.format("YYYY-MM-DD"); .format("YYYY-MM-DD");
delete params.end_date; delete params.end_date;
params.chartGrouping = params.chart_grouping || "daily";
delete params.chart_grouping;
return params; return params;
}, },
@ -57,6 +61,7 @@ export default DiscourseRoute.extend({
start_date: params.startDate start_date: params.startDate
? params.startDate.toISOString(true).split("T")[0] ? params.startDate.toISOString(true).split("T")[0]
: null, : null,
chart_grouping: params.chartGrouping,
filters: params.filters, filters: params.filters,
end_date: params.endDate end_date: params.endDate
? params.endDate.toISOString(true).split("T")[0] ? params.endDate.toISOString(true).split("T")[0]

View File

@ -130,6 +130,18 @@
</div> </div>
{{/if}} {{/if}}
{{#if isChartMode}}
<div class="chart-groupings">
{{#each chartGroupings as |chartGrouping|}}
{{d-button
label=chartGrouping.label
action=(action "changeGrouping" chartGrouping.id)
class=chartGrouping.class
}}
{{/each}}
</div>
{{/if}}
{{#if showDatesOptions}} {{#if showDatesOptions}}
<div class="control"> <div class="control">
<span class="label"> <span class="label">

View File

@ -140,6 +140,18 @@
} }
} }
.chart-groupings {
display: grid;
grid-template-columns: repeat(auto-fill, 1fr);
grid-gap: 0.5em;
margin-bottom: 1em;
.chart-grouping.active {
background: $tertiary;
color: $secondary;
}
}
.control { .control {
margin-bottom: 1em; margin-bottom: 1em;
} }

View File

@ -1,3 +1,9 @@
.admin-report-chart { .admin-report-chart {
animation: fadein 2s; animation: fadein 2s;
.chart-canvas-container {
position: relative;
height: 50vh;
width: 100%;
}
} }

View File

@ -3500,6 +3500,9 @@ en:
view_table: "table" view_table: "table"
view_graph: "graph" view_graph: "graph"
refresh_report: "Refresh Report" refresh_report: "Refresh Report"
daily: Daily
monthly: Monthly
weekly: Weekly
dates: "Dates (UTC)" dates: "Dates (UTC)"
groups: "All groups" groups: "All groups"
disabled: "This report is disabled" disabled: "This report is disabled"
@ -3630,7 +3633,6 @@ en:
optional_allowed_parameters: Allowed Parameters (optional) optional_allowed_parameters: Allowed Parameters (optional)
any_parameter: (any parameter) any_parameter: (any parameter)
web_hooks: web_hooks:
title: "Webhooks" title: "Webhooks"
none: "There are no webhooks right now." none: "There are no webhooks right now."