FIX: quality/bugfix dashboard/reports pass (#6283)

This commit is contained in:
Joffrey JAFFEUX
2018-08-17 16:19:25 +02:00
committed by GitHub
parent 010fe479cb
commit 37d4f27c44
22 changed files with 733 additions and 660 deletions

View File

@ -1,5 +1,4 @@
import computed from "ember-addons/ember-computed-decorators"; import computed from "ember-addons/ember-computed-decorators";
import { registerTooltip, unregisterTooltip } from "discourse/lib/tooltip";
const PAGES_LIMIT = 8; const PAGES_LIMIT = 8;
@ -11,19 +10,6 @@ export default Ember.Component.extend({
perPage: Ember.computed.alias("options.perPage"), perPage: Ember.computed.alias("options.perPage"),
page: 0, page: 0,
didRender() {
this._super(...arguments);
unregisterTooltip($(".text[data-tooltip]"));
registerTooltip($(".text[data-tooltip]"));
},
willDestroyElement() {
this._super(...arguments);
unregisterTooltip($(".text[data-tooltip]"));
},
@computed("model.computedLabels.length") @computed("model.computedLabels.length")
twoColumns(labelsLength) { twoColumns(labelsLength) {
return labelsLength === 2; return labelsLength === 2;
@ -52,7 +38,12 @@ export default Ember.Component.extend({
@computed("totalsForSampleRow", "model.computedLabels") @computed("totalsForSampleRow", "model.computedLabels")
totalsForSample(row, labels) { totalsForSample(row, labels) {
return labels.map(label => label.compute(row)); return labels.map(label => {
const computedLabel = label.compute(row);
computedLabel.type = label.type;
computedLabel.property = label.mainProperty;
return computedLabel;
});
}, },
@computed("model.data", "model.computedLabels") @computed("model.data", "model.computedLabels")
@ -119,7 +110,7 @@ export default Ember.Component.extend({
return { return {
page: v + 1, page: v + 1,
index: v, index: v,
class: v === page ? "current" : null class: v === page ? "is-current" : null
}; };
}); });

View File

@ -4,7 +4,10 @@ import { outputExportResult } from "discourse/lib/export-result";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { SCHEMA_VERSION, default as Report } from "admin/models/report"; import { SCHEMA_VERSION, default as Report } from "admin/models/report";
import computed from "ember-addons/ember-computed-decorators"; import computed from "ember-addons/ember-computed-decorators";
import { registerTooltip, unregisterTooltip } from "discourse/lib/tooltip"; import {
registerHoverTooltip,
unregisterHoverTooltip
} from "discourse/lib/tooltip";
const TABLE_OPTIONS = { const TABLE_OPTIONS = {
perPage: 8, perPage: 8,
@ -35,12 +38,7 @@ function collapseWeekly(data, average) {
} }
export default Ember.Component.extend({ export default Ember.Component.extend({
classNameBindings: [ classNameBindings: ["isEnabled", "isLoading", "dasherizedDataSourceName"],
"isEnabled",
"isLoading",
"dasherizedDataSourceName",
"currentMode"
],
classNames: ["admin-report"], classNames: ["admin-report"],
isEnabled: true, isEnabled: true,
disabledLabel: "admin.dashboard.disabled", disabledLabel: "admin.dashboard.disabled",
@ -69,6 +67,7 @@ export default Ember.Component.extend({
"showDatesOptions", "showDatesOptions",
"showGroupOptions" "showGroupOptions"
), ),
shouldDisplayTrend: Ember.computed.and("showTrend", "model.prev_period"),
init() { init() {
this._super(...arguments); this._super(...arguments);
@ -80,6 +79,7 @@ export default Ember.Component.extend({
this._super(...arguments); this._super(...arguments);
const state = this.get("filters") || {}; const state = this.get("filters") || {};
this.setProperties({ this.setProperties({
category: Category.findById(state.categoryId), category: Category.findById(state.categoryId),
groupId: state.groupId, groupId: state.groupId,
@ -101,14 +101,13 @@ export default Ember.Component.extend({
didRender() { didRender() {
this._super(...arguments); this._super(...arguments);
unregisterTooltip($(".info[data-tooltip]")); registerHoverTooltip($(".info[data-tooltip]"));
registerTooltip($(".info[data-tooltip]"));
}, },
willDestroyElement() { willDestroyElement() {
this._super(...arguments); this._super(...arguments);
unregisterTooltip($(".info[data-tooltip]")); unregisterHoverTooltip($(".info[data-tooltip]"));
}, },
showError: Ember.computed.or("showTimeoutError", "showExceptionError"), showError: Ember.computed.or("showTimeoutError", "showExceptionError"),
@ -140,8 +139,8 @@ export default Ember.Component.extend({
const modes = forcedModes ? forcedModes.split(",") : reportModes; const modes = forcedModes ? forcedModes.split(",") : reportModes;
return Ember.makeArray(modes).map(mode => { return Ember.makeArray(modes).map(mode => {
const base = `mode-button ${mode}`; const base = `mode-btn ${mode}`;
const cssClass = currentMode === mode ? `${base} current` : base; const cssClass = currentMode === mode ? `${base} is-current` : base;
return { return {
mode, mode,
@ -157,7 +156,7 @@ export default Ember.Component.extend({
{ name: I18n.t("admin.dashboard.reports.groups"), value: "all" } { name: I18n.t("admin.dashboard.reports.groups"), value: "all" }
]; ];
return arr.concat( return arr.concat(
this.site.groups.map(i => { (this.site.groups || []).map(i => {
return { name: i["name"], value: i["id"] }; return { name: i["name"], value: i["id"] };
}) })
); );
@ -171,15 +170,25 @@ export default Ember.Component.extend({
@computed("startDate") @computed("startDate")
normalizedStartDate(startDate) { normalizedStartDate(startDate) {
return startDate && typeof startDate.isValid === "function" return startDate && typeof startDate.isValid === "function"
? startDate.format("YYYYMMDD") ? moment
: startDate; .utc(startDate.toISOString())
.locale("en")
.format("YYYYMMDD")
: moment(startDate)
.locale("en")
.format("YYYYMMDD");
}, },
@computed("endDate") @computed("endDate")
normalizedEndDate(endDate) { normalizedEndDate(endDate) {
return endDate && typeof endDate.isValid === "function" return endDate && typeof endDate.isValid === "function"
? endDate.format("YYYYMMDD") ? moment
: endDate; .utc(endDate.toISOString())
.locale("en")
.format("YYYYMMDD")
: moment(endDate)
.locale("en")
.format("YYYYMMDD");
}, },
@computed( @computed(
@ -317,16 +326,15 @@ export default Ember.Component.extend({
let payload = { data: { cache: true, facets } }; let payload = { data: { cache: true, facets } };
if (this.get("startDate")) { if (this.get("startDate")) {
payload.data.start_date = moment( payload.data.start_date = moment
this.get("startDate"), .utc(this.get("startDate"), "YYYY-MM-DD")
"YYYY-MM-DD" .toISOString();
).format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ");
} }
if (this.get("endDate")) { if (this.get("endDate")) {
payload.data.end_date = moment(this.get("endDate"), "YYYY-MM-DD").format( payload.data.end_date = moment
"YYYY-MM-DD[T]HH:mm:ss.SSSZZ" .utc(this.get("endDate"), "YYYY-MM-DD")
); .toISOString();
} }
if (this.get("groupId") && this.get("groupId") !== "all") { if (this.get("groupId") && this.get("groupId") !== "all") {

View File

@ -285,7 +285,7 @@ const Report = Discourse.Model.extend({
value, value,
type, type,
property: mainProperty, property: mainProperty,
formatedValue: value ? escapeExpression(value) : "-" formatedValue: value ? escapeExpression(value) : ""
}; };
} }
}; };
@ -318,7 +318,7 @@ const Report = Discourse.Model.extend({
return { return {
value: username, value: username,
formatedValue: username ? formatedValue(username) : "-" formatedValue: username ? formatedValue(username) : ""
}; };
}, },
@ -333,7 +333,7 @@ const Report = Discourse.Model.extend({
return { return {
value: topicTitle, value: topicTitle,
formatedValue: topicTitle ? formatedValue() : "-" formatedValue: topicTitle ? formatedValue() : ""
}; };
}, },
@ -360,7 +360,7 @@ const Report = Discourse.Model.extend({
_percentLabel(value) { _percentLabel(value) {
return { return {
value, value,
formatedValue: value ? `${value}%` : "-" formatedValue: value ? `${value}%` : ""
}; };
}, },
@ -373,14 +373,14 @@ const Report = Discourse.Model.extend({
return { return {
value, value,
formatedValue: value ? formatedValue() : "-" formatedValue: value ? formatedValue() : ""
}; };
}, },
_dateLabel(value, date) { _dateLabel(value, date) {
return { return {
value, value,
formatedValue: value ? date.format("LL") : "-" formatedValue: value ? date.format("LL") : ""
}; };
}, },
@ -389,7 +389,7 @@ const Report = Discourse.Model.extend({
return { return {
value, value,
formatedValue: value ? escaped : "-" formatedValue: value ? escaped : ""
}; };
}, },
@ -404,7 +404,7 @@ const Report = Discourse.Model.extend({
return { return {
value, value,
formatedValue: value ? formatedValue(value, row[properties[1]]) : "-" formatedValue: value ? formatedValue(value, row[properties[1]]) : ""
}; };
}, },

View File

@ -5,14 +5,23 @@ export default Discourse.Route.extend({
if (!controller.get("start_date")) { if (!controller.get("start_date")) {
controller.set( controller.set(
"start_date", "start_date",
moment() moment
.subtract("30", "day") .utc()
.subtract(1, "day")
.subtract(1, "month")
.startOf("day")
.format("YYYY-MM-DD") .format("YYYY-MM-DD")
); );
} }
if (!controller.get("end_date")) { if (!controller.get("end_date")) {
controller.set("end_date", moment().format("YYYY-MM-DD")); controller.set(
"end_date",
moment()
.utc()
.endOf("day")
.format("YYYY-MM-DD")
);
} }
} }
}); });

View File

@ -1,5 +1,5 @@
{{#if showSortingUI}} {{#if showSortingUI}}
{{d-button action=sortByLabel icon=sortIcon class="sort-button"}} {{d-button action=sortByLabel icon=sortIcon class="sort-btn"}}
{{/if}} {{/if}}
<span>{{label.title}}</span> <span class="title">{{label.title}}</span>

View File

@ -1,4 +1,4 @@
<table class="report-table"> <table class="table">
<thead> <thead>
<tr> <tr>
{{#if model.computedLabels}} {{#if model.computedLabels}}
@ -30,7 +30,7 @@
</tr> </tr>
<tr class="admin-report-table-row"> <tr class="admin-report-table-row">
{{#each totalsForSample as |total|}} {{#each totalsForSample as |total|}}
<td class="{{total.type}} {{total.property}}"> <td class="admin-report-table-cell {{total.type}} {{total.property}}">
{{total.formatedValue}} {{total.formatedValue}}
</td> </td>
{{/each}} {{/each}}
@ -44,8 +44,8 @@
</td> </td>
</tr> </tr>
<tr class="admin-report-table-row"> <tr class="admin-report-table-row">
<td class="date x">-</td> <td class="admin-report-table-cell date x"></td>
<td class="number y">{{number model.total}}</td> <td class="admin-report-table-cell number y">{{number model.total}}</td>
</tr> </tr>
{{/if}} {{/if}}
</tbody> </tbody>

View File

@ -1,34 +1,35 @@
{{#if isEnabled}} {{#if isEnabled}}
{{#conditional-loading-section isLoading=isLoading}} {{#conditional-loading-section isLoading=isLoading}}
{{#if showHeader}} {{#if showHeader}}
<div class="report-header"> <div class="header">
{{#if showTitle}} {{#if showTitle}}
<div class="report-title"> <ul class="breadcrumb">
<h3 class="title">
{{#if showAllReportsLink}} {{#if showAllReportsLink}}
{{#link-to "adminReports" class="all-report-link"}} <li class="item all-reports">
{{#link-to "adminReports" class="report-url"}}
{{i18n "admin.dashboard.all_reports"}} {{i18n "admin.dashboard.all_reports"}}
{{/link-to}} {{/link-to}}
<span class="separator">|</span> </li>
<li class="item separator">|</li>
{{/if}} {{/if}}
<a href="{{model.reportUrl}}" class="report-link"> <li class="item report">
<a href="{{model.reportUrl}}" class="report-url">
{{model.title}} {{model.title}}
</a> </a>
</h3>
{{#if model.description}} {{#if model.description}}
<span class="info" data-tooltip="{{model.description}}"> <span class="info" data-tooltip="{{model.description}}">
{{d-icon "question-circle"}} {{d-icon "question-circle"}}
</span> </span>
{{/if}} {{/if}}
</div> </li>
</ul>
{{/if}} {{/if}}
{{#if showTrend}} {{#if shouldDisplayTrend}}
{{#if model.prev_period}}
<div class="trend {{model.trend}}"> <div class="trend {{model.trend}}">
<span class="trend-value" title="{{model.trendTitle}}"> <span class="value" title="{{model.trendTitle}}">
{{#if model.average}} {{#if model.average}}
{{number model.currentAverage}}{{#if model.percent}}%{{/if}} {{number model.currentAverage}}{{#if model.percent}}%{{/if}}
{{else}} {{else}}
@ -37,14 +38,62 @@
</span> </span>
{{#if model.trendIcon}} {{#if model.trendIcon}}
{{d-icon model.trendIcon class="trend-icon"}} {{d-icon model.trendIcon class="icon"}}
{{/if}} {{/if}}
</div> </div>
{{/if}} {{/if}}
</div>
{{/if}} {{/if}}
<div class="body">
<div class="main">
{{#unless showError}}
{{#if hasData}}
{{#if currentMode}}
{{component modeComponent model=model options=options}}
{{#if model.relatedReport}}
{{admin-report showFilteringUI=false dataSourceName=model.relatedReport.type}}
{{/if}}
{{/if}}
{{else}}
<div class="alert alert-info report-alert no-data">
{{d-icon "pie-chart"}}
{{#if model.reportUrl}}
<a href="{{model.reportUrl}}" class="report-url">
<span>
{{#if model.title}}
{{model.title}}
{{/if}}
{{i18n "admin.dashboard.reports.no_data"}}
</span>
</a>
{{else}}
<span>{{i18n "admin.dashboard.reports.no_data"}}</span>
{{/if}}
</div>
{{/if}}
{{else}}
{{#if showTimeoutError}}
<div class="alert alert-error report-alert timeout">
{{d-icon "exclamation-triangle"}}
<span>{{i18n "admin.dashboard.timeout_error"}}</span>
</div>
{{/if}}
{{#if showExceptionError}}
<div class="alert alert-error report-alert exception">
{{d-icon "exclamation-triangle"}}
<span>{{i18n "admin.dashboard.exception_error"}}</span>
</div>
{{/if}}
{{/unless}}
</div>
{{#if showFilteringUI}}
<div class="filters">
{{#if showModes}} {{#if showModes}}
<ul class="mode-switch"> <ul class="modes">
{{#each displayedModes as |displayedMode|}} {{#each displayedModes as |displayedMode|}}
<li class="mode"> <li class="mode">
{{d-button {{d-button
@ -56,56 +105,26 @@
{{/each}} {{/each}}
</ul> </ul>
{{/if}} {{/if}}
</div>
{{/if}}
<div class="report-body">
{{#unless showError}}
{{#if hasData}}
{{#if currentMode}}
{{component modeComponent model=model options=options}}
{{/if}}
{{else}}
<div class="alert alert-info no-data">
{{d-icon "pie-chart"}}
<span>{{i18n "admin.dashboard.reports.no_data"}}</span>
</div>
{{/if}}
{{else}}
{{#if showTimeoutError}}
<div class="alert alert-error report-error timeout">
<span>{{i18n "admin.dashboard.timeout_error"}}</span>
</div>
{{/if}}
{{#if showExceptionError}}
<div class="alert alert-error report-error exception">
{{i18n "admin.dashboard.exception_error"}}
</div>
{{/if}}
{{/unless}}
{{#if showFilteringUI}}
<div class="report-filters">
{{#if showDatesOptions}} {{#if showDatesOptions}}
<div class="filtering-control"> <div class="control">
<span class="filtering-label"> <span class="label">
{{i18n 'admin.dashboard.reports.start_date'}} {{i18n 'admin.dashboard.reports.start_date'}}
</span> </span>
<div class="filtering-input"> <div class="input">
{{date-picker-past {{date-picker-past
value=startDate value=startDate
defaultDate=startDate}} defaultDate=startDate}}
</div> </div>
</div> </div>
<div class="filtering-control"> <div class="control">
<span class="filtering-label"> <span class="label">
{{i18n 'admin.dashboard.reports.end_date'}} {{i18n 'admin.dashboard.reports.end_date'}}
</span> </span>
<div class="filtering-input"> <div class="input">
{{date-picker-past {{date-picker-past
value=endDate value=endDate
defaultDate=endDate}} defaultDate=endDate}}
@ -114,8 +133,8 @@
{{/if}} {{/if}}
{{#if showCategoryOptions}} {{#if showCategoryOptions}}
<div class="filtering-control"> <div class="control">
<div class="filtering-input"> <div class="input">
{{search-advanced-category-chooser {{search-advanced-category-chooser
filterable=true filterable=true
value=category value=category
@ -125,8 +144,8 @@
{{/if}} {{/if}}
{{#if showGroupOptions}} {{#if showGroupOptions}}
<div class="filtering-control"> <div class="control">
<div class="filtering-input"> <div class="input">
{{combo-box {{combo-box
castInteger=true castInteger=true
filterable=true filterable=true
@ -138,8 +157,8 @@
{{/if}} {{/if}}
{{#if showExport}} {{#if showExport}}
<div class="filtering-control"> <div class="control">
<div class="filtering-input"> <div class="input">
{{d-button {{d-button
class="export-csv-btn" class="export-csv-btn"
action="exportCsv" action="exportCsv"
@ -150,8 +169,8 @@
{{/if}} {{/if}}
{{#if showRefresh}} {{#if showRefresh}}
<div class="filtering-control"> <div class="control">
<div class="filtering-input"> <div class="input">
{{d-button {{d-button
class="refresh-report-btn btn-primary" class="refresh-report-btn btn-primary"
action="refreshReport" action="refreshReport"
@ -163,10 +182,6 @@
</div> </div>
{{/if}} {{/if}}
</div> </div>
{{#if model.relatedReport}}
{{admin-report dataSourceName=model.relatedReport.type}}
{{/if}}
{{/conditional-loading-section}} {{/conditional-loading-section}}
{{else}} {{else}}
<div class="alert alert-info"> <div class="alert alert-info">

View File

@ -57,23 +57,23 @@
<div class="section-columns"> <div class="section-columns">
<div class="section-column"> <div class="section-column">
<div class="admin-report activity-metrics"> <div class="admin-report activity-metrics">
<div class="report-header"> <div class="header">
<div class="report-title"> <ul class="breadcrumb">
<h3 class="title"> <li class="item report">
{{#link-to "adminReports" class="report-link"}} {{#link-to "adminReports" class="report-url"}}
{{i18n "admin.dashboard.activity_metrics"}} {{i18n "admin.dashboard.activity_metrics"}}
{{/link-to}} {{/link-to}}
</h3> </li>
</div> </ul>
</div> </div>
<div class="report-body"> <div class="report-body">
<div class="admin-report-counters-list"> <div class="counters-list">
<div class="counters-header"> <div class="counters-header">
<div class="header"></div> <div class="counters-cell"></div>
<div class="header">{{i18n 'admin.dashboard.reports.today'}}</div> <div class="counters-cell">{{i18n 'admin.dashboard.reports.today'}}</div>
<div class="header">{{i18n 'admin.dashboard.reports.yesterday'}}</div> <div class="counters-cell">{{i18n 'admin.dashboard.reports.yesterday'}}</div>
<div class="header">{{i18n 'admin.dashboard.reports.last_7_days'}}</div> <div class="counters-cell">{{i18n 'admin.dashboard.reports.last_7_days'}}</div>
<div class="header">{{i18n 'admin.dashboard.reports.last_30_days'}}</div> <div class="counters-cell">{{i18n 'admin.dashboard.reports.last_30_days'}}</div>
</div> </div>
{{#each activityMetrics as |metric|}} {{#each activityMetrics as |metric|}}
@ -84,10 +84,11 @@
{{/each}} {{/each}}
</div> </div>
</div> </div>
</div>
{{#link-to "adminReports"}} {{#link-to "adminReports"}}
{{i18n "admin.dashboard.all_reports"}} {{i18n "admin.dashboard.all_reports"}}
{{/link-to}} {{/link-to}}
</div>
<div class="user-metrics"> <div class="user-metrics">
{{#conditional-loading-section isLoading=isLoading}} {{#conditional-loading-section isLoading=isLoading}}

View File

@ -1,8 +1,9 @@
import { escapeExpression } from "discourse/lib/utilities"; import { escapeExpression } from "discourse/lib/utilities";
export function showTooltip() {
const fadeSpeed = 300; const fadeSpeed = 300;
const tooltipID = "#discourse-tooltip"; const tooltipID = "#discourse-tooltip";
export function showTooltip() {
const $this = $(this); const $this = $(this);
const $parent = $this.offsetParent(); const $parent = $this.offsetParent();
const content = escapeExpression($this.attr("data-tooltip")); const content = escapeExpression($this.attr("data-tooltip"));
@ -16,9 +17,7 @@ export function showTooltip() {
pos.top -= delta.top; pos.top -= delta.top;
pos.left -= delta.left; pos.left -= delta.left;
$(tooltipID) hideTooltip(tooltipID);
.fadeOut(fadeSpeed)
.remove();
$(this).after(` $(this).after(`
<div id="discourse-tooltip" ${retina}> <div id="discourse-tooltip" ${retina}>
@ -67,9 +66,24 @@ export function showTooltip() {
return false; return false;
} }
export function hideTooltip() {
$(tooltipID)
.fadeOut(fadeSpeed)
.remove();
}
export function registerTooltip(jqueryContext) { export function registerTooltip(jqueryContext) {
if (jqueryContext.length) { if (jqueryContext.length) {
jqueryContext.on("click", showTooltip); jqueryContext.off("click").on("click", showTooltip);
}
}
export function registerHoverTooltip(jqueryContext) {
if (jqueryContext.length) {
jqueryContext
.off("mouseenter mouseleave click")
.on("mouseenter click", showTooltip)
.on("mouseleave", hideTooltip);
} }
} }
@ -78,3 +92,9 @@ export function unregisterTooltip(jqueryContext) {
jqueryContext.off("click"); jqueryContext.off("click");
} }
} }
export function unregisterHoverTooltip(jqueryContext) {
if (jqueryContext.length) {
jqueryContext.off("mouseenter mouseleave click");
}
}

View File

@ -1,72 +1,38 @@
.admin-report { .admin-report {
.report-error,
.no-data {
width: 100%;
width: 100%;
align-self: flex-start;
text-align: center;
padding: 3em;
margin-bottom: 1.5em;
box-sizing: border-box;
}
+ .table {
margin-top: 1.5em;
}
.report-error {
color: $danger;
border: 1px solid $danger;
}
.no-data {
background: $secondary;
border: 1px solid $primary-low;
color: $primary-low-mid;
.d-icon-pie-chart {
color: currentColor;
margin-bottom: 0.25em;
font-size: $font-up-5;
display: block;
}
}
.conditional-loading-section { .conditional-loading-section {
flex: 1; &.is-loading {
margin: 0; margin: 0;
} }
}
.report-header { .header {
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
margin-top: 0.5em; border-bottom: 1px solid $primary-low;
margin-bottom: 0.5em; margin-bottom: 0.5em;
padding-bottom: 0.5em;
}
.report-title { .header .breadcrumb {
align-items: center;
display: flex;
justify-content: space-between;
.title {
margin: 0; margin: 0;
padding: 0; list-style: none;
border: 0;
.item {
display: inline;
font-size: $font-up-1; font-size: $font-up-1;
.separator {
font-weight: normal;
} }
.report-link { .all-reports .report-url {
font-weight: 700;
}
.report {
font-weight: 700;
.report-url {
color: $primary; color: $primary;
} }
.separator + .report-link {
font-weight: normal;
}
}
.info { .info {
cursor: pointer; cursor: pointer;
margin-left: 0.25em; margin-left: 0.25em;
@ -77,10 +43,10 @@
} }
} }
} }
}
.trend { .header .trend {
align-items: center; margin-left: auto;
&.trending-down, &.trending-down,
&.high-trending-down { &.high-trending-down {
color: $danger; color: $danger;
@ -95,149 +61,116 @@
color: $primary-medium; color: $primary-medium;
} }
.trend-value { .value {
font-size: $font-up-1; font-size: $font-up-1;
} }
.trend-icon { .icon {
font-size: $font-up-1; font-size: $font-up-1;
font-weight: 700; font-weight: 700;
} }
} }
.mode-switch { .body {
list-style: none;
display: flex; display: flex;
}
.main {
flex: 1;
}
.main .report-alert {
margin: 0; margin: 0;
text-align: center;
padding: 3em;
border: 1px solid transparent;
a {
color: $primary-medium;
}
.d-icon {
color: currentColor;
margin-bottom: 0.25em;
font-size: $font-up-5;
display: block;
}
&.no-data {
background: $secondary;
border-color: $primary-low;
color: $primary-low-mid;
}
&.timeout,
&.exception {
border-color: $danger-low;
color: $danger;
}
}
.filters {
display: flex;
margin-left: 1em;
flex-direction: column;
width: 220px;
.modes {
margin: 0 0 1em 0;
padding: 0;
list-style: none;
.mode { .mode {
display: inline; display: inline-flex;
flex: 1;
.mode-button.current { .mode-btn.is-current {
color: $tertiary; color: $tertiary;
} }
} }
} }
}
.report-body { .control {
display: flex;
justify-content: space-between;
.admin-report-table,
.admin-report-chart {
width: 100%;
}
.report-filters {
margin-left: 1em;
min-width: 250px;
display: flex;
flex-direction: column;
.filtering-control {
display: flex;
flex-direction: column;
margin-bottom: 1em; margin-bottom: 1em;
} }
.filtering-label {
.control .label {
font-weight: 700;
width: 100%;
} }
.filtering-input {
.control .input,
.control .select-kit {
width: 100%; width: 100%;
.date-picker-wrapper, .export-csv-btn {
.combo-box, width: 100%;
.export-csv-btn, }
.refresh-report-btn { .refresh-report-btn {
width: 100%; width: 100%;
} }
.date-picker-wrapper { .date-picker-wrapper {
.date-picker {
width: 100%; width: 100%;
.date-picker {
box-sizing: border-box; box-sizing: border-box;
width: 100%;
margin: 0; margin: 0;
} }
} }
} }
} }
.report-filters:only-child {
margin-left: auto;
}
}
} }
.admin-report.activity-metrics { .rtl .admin-report {
table { .filters {
table-layout: auto; margin-left: 0;
} margin-right: 1em;
} }
.admin-report.users-by-type { .trend {
margin-top: 1.5em; margin-left: unset;
} margin-right: auto;
.admin-report.users-by-type,
.admin-report.users-by-trust-level {
margin-bottom: 1.5em;
flex: 1;
.report-header {
border-bottom: 1px solid $primary-medium;
padding-bottom: 0.25em;
border-bottom: 1px solid #e9e9e9;
}
}
.admin-report.moderators-activity {
tbody tr td.username,
thead tr th.username {
text-align: left;
}
}
.admin-report.trending-search {
tbody tr td.term,
thead tr th.term {
text-align: left;
}
}
.admin-report.top-traffic-sources {
tbody tr td.domain,
thead tr th.domain {
text-align: left;
}
}
.admin-report.post-edits {
.report-table {
table-layout: auto;
tbody tr td,
thead tr th {
text-align: left;
}
thead tr th.edit_reason,
tbody tr td.edit_reason {
width: 100%;
}
}
}
.admin-report.flags-status {
.admin-report-table {
table-layout: auto;
tbody tr td,
thead tr th {
text-align: left;
}
tbody tr td.response_time,
thead tr th.response_time {
text-align: center;
}
} }
} }

View File

@ -1,67 +1,21 @@
.admin-report-counters-list { .admin-report {
display: flex;
flex: 1;
flex-direction: column;
border-bottom: 1px solid $primary-low;
.counters-header {
display: grid;
flex: 1;
grid-template-columns: 33% repeat(auto-fit, minmax(20px, 1fr));
border: 1px solid $primary-low;
border-bottom: 0;
padding: 0.25em;
font-weight: 700;
text-align: right;
}
.conditional-loading-section.is-loading {
padding: 0.5em;
margin: 0;
flex-direction: row;
justify-content: flex-start;
.title {
font-weight: normal;
font-size: $font-down-1;
}
.spinner {
margin: 0 0 0 0.5em;
height: 5px;
width: 5px;
}
}
}
.admin-report.counters {
.admin-report-counters { .admin-report-counters {
display: grid; display: grid;
flex: 1; flex: 1;
grid-template-columns: 33% repeat(auto-fit, minmax(20px, 1fr)); grid-template-columns: 33% repeat(auto-fit, minmax(20px, 1fr));
grid-template-rows: repeat(auto-fit, minmax(32px, 1fr)); grid-template-rows: repeat(auto-fit, minmax(32px, 1fr));
border: 1px solid $primary-low;
align-items: center; align-items: center;
border-bottom: 0;
.cell { .cell {
padding: 0.25em; padding: 0.25em;
text-align: right; text-align: right;
white-space: nowrap; white-space: nowrap;
padding: 8px 21px 8px 8px; // accounting for negative right caret margin
&:nth-of-type(2) {
padding: 8px 12px 8px;
}
i {
margin-right: -12px; // align on caret
@media screen and (max-width: 650px) {
margin-right: -9px;
}
}
&.title { &.title {
text-align: left; text-align: left;
padding: 8px; text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
.d-icon { .d-icon {
color: $primary-low-mid; color: $primary-low-mid;
@ -75,15 +29,10 @@
} }
@media screen and (max-width: 400px) { @media screen and (max-width: 400px) {
&.title { &.title .d-icon {
padding: 8px 0 8px 4px;
font-size: $font-down-1;
.d-icon {
display: none; display: none;
} }
} }
}
&.high-trending-up, &.high-trending-up,
&.trending-up { &.trending-up {
@ -99,27 +48,18 @@
} }
} }
} }
.no-data {
margin: 0;
padding: 8px;
display: flex;
flex-direction: row;
align-items: center;
font-size: $font-0;
border-bottom: 0;
color: $primary-medium;
.d-icon {
font-size: $font-up-1;
margin: 0 0.25em 0 0;
color: $primary-low-mid;
}
} }
.alert-error { .rtl .counters-list .counters-header .counters-cell {
text-align: left; text-align: left;
padding: 0.5em; }
margin: 0;
border: 0; .rtl .counters-list {
.cell {
text-align: left;
&.title {
text-align: right;
}
} }
} }

View File

@ -1,9 +1,74 @@
.admin-report-table { .admin-report-table {
@media screen and (max-width: 650px) { &.two-columns {
table { .table .admin-report-table-cell:first-child,
tbody tr td { .table .admin-report-table-header:first-child {
font-size: $font-down-1; text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
text-align: left;
width: 80%;
} }
.table .admin-report-table-cell:last-child,
.table .admin-report-table-header:last-child {
display: inline-block;
width: 80%;
text-align: right;
}
}
.table {
margin: 0;
border: 1px solid $primary-low;
table-layout: fixed;
tbody {
border-top: 0;
}
}
.table .admin-report-table-header {
.sort-btn {
outline: none;
background: none;
padding: 3px 8px;
overflow: hidden;
text-overflow: ellipsis;
}
&.is-current-sort {
.d-icon {
color: $tertiary;
}
.sort-btn:hover {
color: $primary-medium;
background: $primary-low;
}
}
&:not(.is-current-sort) .sort-btn {
background: none;
&:hover {
color: $primary-medium;
background: $primary-low;
}
}
}
.admin-report-table-cell {
&.user .username {
margin-left: 0.25em;
}
}
.total-row {
background: $primary-very-low;
td {
font-weight: 700;
text-align: left;
} }
} }
@ -15,98 +80,114 @@
button { button {
margin-left: 0.5em; margin-left: 0.5em;
&.current { &.is-current {
color: $tertiary; color: $tertiary;
} }
} }
} }
}
.admin-report.top-referred-topics {
.admin-report-table-header.topic_title {
width: 80%;
}
}
.admin-report.trending-search {
.admin-report-table-header.ctr,
.admin-report-table-header.unique_searches,
.admin-report-table-cell.ctr,
.admin-report-table-cell.unique_searches {
text-align: center;
width: 20%;
}
.admin-report-table-cell.term {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
.admin-report.moderators-activity {
.admin-report-table-header.seconds,
.admin-report-table-header.number,
.admin-report-table-cell.seconds,
.admin-report-table-cell.number {
text-align: center;
}
.admin-report-table-header.user {
width: 20%;
}
}
.admin-report.post-edits {
.admin-report-table-header.user {
width: 20%;
}
.admin-report-table-cell.post,
.admin-report-table-cell.edit_reason {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
.admin-report.flags-status {
.admin-report-table-header.response_time,
.admin-report-table-cell.response_time {
text-align: center;
}
}
.rtl {
.admin-report-table {
&.two-columns { &.two-columns {
.report-table tbody tr td:first-child, .table .admin-report-table-cell:first-child,
.report-table thead tr th:first-child { .table .admin-report-table-header:first-child {
text-align: right;
}
.table .admin-report-table-cell:last-child,
.table .admin-report-table-header:last-child {
text-align: left; text-align: left;
} }
.report-table {
table-layout: auto;
} }
}
.report-table {
table-layout: fixed;
border: 1px solid $primary-low;
margin-top: 0;
tbody {
border: none;
.total-row { .total-row {
td { td {
font-weight: 700; text-align: right;
text-align: left;
}
}
tr {
td {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
text-align: center;
padding: 8px;
&.user {
text-align: left;
.username {
margin-left: 3px;
}
}
}
}
}
thead {
border: 1px solid $primary-low;
.admin-report-table-header {
.sort-button {
outline: none;
background: none;
padding: 3px 7px;
overflow: hidden;
text-overflow: ellipsis;
}
&.is-current-sort {
.d-icon {
color: $tertiary;
}
.sort-button:hover {
color: $primary-medium;
background: $primary-low;
}
}
&:not(.is-current-sort) .sort-button {
background: none;
&:hover {
color: $primary-medium;
background: $primary-low;
} }
} }
} }
tr { .admin-report-table-cell {
th { &.user .username {
text-align: center; margin-left: 0;
text-overflow: ellipsis; margin-right: 0.25em;
overflow: hidden; }
white-space: nowrap; }
}
} .admin-report.trending-search {
.admin-report-table-header.term,
.admin-report-table-cell.term {
text-align: right;
}
}
.pagination {
button {
margin-left: 0;
margin-right: 0.5em;
}
}
.admin-report.moderators-activity {
.admin-report-table-header.user,
.admin-report-table-cell.user {
text-align: right;
} }
} }
} }

View File

@ -62,11 +62,11 @@
max-width: 100%; max-width: 100%;
&:last-child { &:last-child {
margin-left: 1em; margin-left: 0.5em;
} }
&:first-child { &:first-child {
margin-right: 1em; margin-right: 0.5em;
} }
@include breakpoint(medium) { @include breakpoint(medium) {
@ -119,22 +119,25 @@
} }
} }
.charts { .admin-report .header {
display: flex; border: 0;
justify-content: space-between; padding: 0;
flex-wrap: wrap; margin-bottom: 1em;
}
.chart { .charts {
max-width: calc(100% * 1 / 3.2); display: grid;
width: 100%; grid-template-columns: repeat(12, 1fr);
flex-grow: 1; grid-column-gap: 1em;
flex-basis: 100%; grid-row-gap: 1em;
display: flex;
.admin-report {
grid-column: span 4;
} }
@include breakpoint(medium) { @include breakpoint(medium) {
.chart { .admin-report {
max-width: 100%; grid-column: span 12;
} }
} }
@ -209,6 +212,10 @@
} }
} }
.top-referred-topics {
margin-bottom: 1.5em;
}
.top-referred-topics, .top-referred-topics,
.trending-search { .trending-search {
th:first-of-type { th:first-of-type {
@ -216,12 +223,6 @@
} }
} }
.top-referred-topics {
.dashboard-table table {
table-layout: auto;
}
}
.section { .section {
.period-chooser .period-chooser-header { .period-chooser .period-chooser-header {
.selected-name, .selected-name,
@ -248,27 +249,82 @@
} }
} }
.admin-report-table { .counters-list {
&.is-disabled { display: flex;
background: $primary-low; flex: 1;
padding: 1em; flex-direction: column;
.counters-header {
display: grid;
flex: 1;
grid-template-columns: 33% repeat(auto-fit, minmax(20px, 1fr));
border: 1px solid $primary-low;
border-bottom: 0;
font-weight: 700;
text-align: right;
align-items: center;
padding: 0.65em 0.25em;
} }
@media screen and (max-width: 650px) { .admin-report .main {
table { border: 1px solid $primary-low;
tbody tr td {
font-size: $font-down-1; &:hover {
} background-color: $primary-very-low;
} }
} }
&.is-loading { .admin-report:not(:last-child) {
height: 150px; .main {
border-bottom: 0;
}
.conditional-loading-section.is-loading {
border-bottom: 0;
}
}
.admin-report .conditional-loading-section.is-loading {
display: flex;
flex-direction: row;
padding: 0.5em 0.25em;
align-items: flex-start;
justify-content: flex-start;
border: 1px solid $primary-low;
.title {
font-size: $font-0;
}
.spinner {
margin: 0;
width: 8px;
height: 8px;
margin-left: 0.5em;
}
}
.admin-report .main .report-alert {
display: flex;
flex-direction: row;
padding: 0.5em 0.25em;
align-items: center;
border: 0;
&:hover {
background-color: $primary-very-low;
}
.d-icon {
font-size: $font-up-1;
margin: 0 0.25em 0 0;
color: $primary-low-mid;
}
} }
} }
.activity-metrics { .activity-metrics {
margin-bottom: 0.25em; margin-bottom: 1.5em;
} }
.user-metrics { .user-metrics {
@ -368,21 +424,32 @@
} }
} }
.community-health.section { .users-by-trust-level,
margin-bottom: 1em; .users-by-type {
margin-bottom: 1.5em;
} }
.dashboard-next.moderation { .community-health.section {
margin-bottom: 1.5em;
}
.dashboard-next-moderation {
.admin-dashboard-moderation-top { .admin-dashboard-moderation-top {
display: grid; display: grid;
grid-template-columns: repeat(12, 1fr); grid-template-columns: repeat(12, 1fr);
grid-column-gap: 1em; grid-column-gap: 1em;
grid-row-gap: 1em;
}
.section-body {
margin-bottom: 1em;
} }
.main-section { .main-section {
display: grid; display: grid;
grid-template-columns: repeat(12, 1fr); grid-template-columns: repeat(12, 1fr);
grid-column-gap: 1em; grid-column-gap: 1em;
grid-row-gap: 1em;
> * { > * {
grid-column: span 12; grid-column: span 12;
@ -392,6 +459,7 @@
display: grid; display: grid;
grid-template-columns: repeat(12, 1fr); grid-template-columns: repeat(12, 1fr);
grid-column-gap: 1em; grid-column-gap: 1em;
grid-row-gap: 1em;
} }
} }

View File

@ -22,9 +22,11 @@
@import "mobile/ring"; @import "mobile/ring";
@import "mobile/group"; @import "mobile/group";
@import "mobile/groups"; @import "mobile/groups";
@import "mobile/dashboard_next";
@import "mobile/admin_reports"; @import "mobile/admin_reports";
@import "mobile/admin_report"; @import "mobile/admin_report";
@import "mobile/admin_report_table"; @import "mobile/admin_report_table";
@import "mobile/admin_report_counters";
// Import all component-specific files // Import all component-specific files
@import "mobile/components/*"; @import "mobile/components/*";

View File

@ -9,27 +9,9 @@
} }
} }
.admin-report.top-referred-topics {
.admin-report-table {
.report-table {
table-layout: fixed;
thead tr th.topic_title,
tbody tr td.topic_title {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 80%;
}
}
}
}
.admin-report.trending-search { .admin-report.trending-search {
.admin-report-table { .admin-report-table {
.report-table { .report-table {
table-layout: fixed;
thead tr th.term, thead tr th.term,
tbody tr td.term { tbody tr td.term {
width: 50%; width: 50%;

View File

@ -0,0 +1,5 @@
.counters-list {
.counters-header .counters-cell {
font-weight: normal;
}
}

View File

@ -1,14 +1,28 @@
.admin-report-table { .admin-report-table {
.report-table { .table {
table-layout: fixed; font-size: $font-down-1;
}
thead tr th { .table .admin-report-table-header {
font-weight: normal; font-weight: 500;
font-size: $font-down-2; border-right: 1px solid $primary-low;
white-space: unset; padding: auto;
.sort-button {
.title {
writing-mode: vertical-rl;
text-orientation: mixed;
text-align: right;
transform: rotate(180deg);
}
.sort-btn {
display: none;
}
}
.table tbody tr td {
&.user .username {
display: none; display: none;
} }
} }
} }
}

View File

@ -1,24 +1,24 @@
.admin-reports { .admin-reports {
.admin-report { .admin-report {
.report-body { .body {
flex-direction: column; flex-direction: column;
.report-filters { .filters {
order: 0; order: 0;
margin: 0; margin: 0;
width: 100%;
} }
.alert { .main {
order: 2;
}
.report-alert {
margin: 0; margin: 0;
order: 1; order: 1;
flex: 1; flex: 1;
padding: 1em; padding: 1em;
} }
.admin-report-table,
.admin-report-chart {
order: 2;
}
} }
} }
} }

View File

@ -0,0 +1,5 @@
.dashboard-next {
.activity-metrics .counters-list {
font-size: $font-down-1;
}
}

View File

@ -23,8 +23,8 @@ class Admin::ReportsController < Admin::AdminController
raise Discourse::NotFound unless report_type =~ /^[a-z0-9\_]+$/ raise Discourse::NotFound unless report_type =~ /^[a-z0-9\_]+$/
start_date = (params[:start_date].present? ? params[:start_date].to_date : 30.days.ago).beginning_of_day start_date = (params[:start_date].present? ? Time.parse(params[:start_date]).to_date : 1.days.ago).beginning_of_day
end_date = (params[:end_date].present? ? params[:end_date].to_date : start_date + 30.days).end_of_day end_date = (params[:end_date].present? ? Time.parse(params[:end_date]).to_date : start_date + 30.days).end_of_day
if params.has_key?(:category_id) && params[:category_id].to_i > 0 if params.has_key?(:category_id) && params[:category_id].to_i > 0
category_id = params[:category_id].to_i category_id = params[:category_id].to_i

View File

@ -18,8 +18,8 @@ class Report
def initialize(type) def initialize(type)
@type = type @type = type
@start_date ||= Report.default_days.days.ago.beginning_of_day @start_date ||= Report.default_days.days.ago.utc.beginning_of_day
@end_date ||= Time.now.end_of_day @end_date ||= Time.now.utc.end_of_day
@prev_end_date = @start_date @prev_end_date = @start_date
@average = false @average = false
@percent = false @percent = false

View File

@ -10,12 +10,10 @@ componentTest("default", {
async test(assert) { async test(assert) {
assert.ok(exists(".admin-report.signups")); assert.ok(exists(".admin-report.signups"));
assert.ok( assert.ok(exists(".admin-report.signups", "it defaults to table mode"));
exists(".admin-report.table.signups", "it defaults to table mode")
);
assert.equal( assert.equal(
find(".report-header .title") find(".header .item.report")
.text() .text()
.trim(), .trim(),
"Signups", "Signups",
@ -23,13 +21,13 @@ componentTest("default", {
); );
assert.equal( assert.equal(
find(".report-header .info").attr("data-tooltip"), find(".header .info").attr("data-tooltip"),
"New account registrations for this period", "New account registrations for this period",
"it has a description" "it has a description"
); );
assert.equal( assert.equal(
find(".report-body .report-table thead tr th:first-child") find(".admin-report-table thead tr th:first-child .title")
.text() .text()
.trim(), .trim(),
"Day", "Day",
@ -37,7 +35,7 @@ componentTest("default", {
); );
assert.equal( assert.equal(
find(".report-body .report-table thead tr th:nth-child(2)") find(".admin-report-table thead tr th:nth-child(2) .title")
.text() .text()
.trim(), .trim(),
"Count", "Count",
@ -45,7 +43,7 @@ componentTest("default", {
); );
assert.equal( assert.equal(
find(".report-body .report-table tbody tr:nth-child(1) td:nth-child(1)") find(".admin-report-table tbody tr:nth-child(1) td:nth-child(1)")
.text() .text()
.trim(), .trim(),
"June 16, 2018", "June 16, 2018",
@ -53,7 +51,7 @@ componentTest("default", {
); );
assert.equal( assert.equal(
find(".report-body .report-table tbody tr:nth-child(1) td:nth-child(2)") find(".admin-report-table tbody tr:nth-child(1) td:nth-child(2)")
.text() .text()
.trim(), .trim(),
"12", "12",
@ -62,9 +60,10 @@ componentTest("default", {
assert.ok(exists(".total-row"), "it has totals"); assert.ok(exists(".total-row"), "it has totals");
await click(".admin-report-table-header.y .sort-button"); await click(".admin-report-table-header.y .sort-btn");
assert.equal( assert.equal(
find(".report-body .report-table tbody tr:nth-child(1) td:nth-child(2)") find(".admin-report-table tbody tr:nth-child(1) td:nth-child(2)")
.text() .text()
.trim(), .trim(),
"7", "7",
@ -98,13 +97,13 @@ componentTest("options", {
}); });
componentTest("switch modes", { componentTest("switch modes", {
template: "{{admin-report dataSourceName='signups'}}", template: "{{admin-report dataSourceName='signups' showFilteringUI=true}}",
async test(assert) { async test(assert) {
await click(".mode-button.chart"); await click(".mode-btn.chart");
assert.notOk(exists(".admin-report.table.signups"), "it removes the table"); assert.notOk(exists(".admin-report-table"), "it removes the table");
assert.ok(exists(".admin-report.chart.signups"), "it shows the chart"); assert.ok(exists(".admin-report-chart"), "it shows the chart");
} }
}); });