mirror of
https://github.com/discourse/discourse.git
synced 2025-06-07 08:14:39 +08:00
UX: refactoring/refining tables of new dashboard
This commit is contained in:
@ -1,32 +1,13 @@
|
|||||||
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";
|
import AsyncReport from "admin/mixins/async-report";
|
||||||
|
|
||||||
export default Ember.Component.extend(AsyncReport, {
|
export default Ember.Component.extend(AsyncReport, {
|
||||||
classNames: ["dashboard-table", "dashboard-inline-table", "fixed"],
|
classNames: ["dashboard-inline-table"],
|
||||||
help: null,
|
|
||||||
helpPage: null,
|
|
||||||
|
|
||||||
loadReport(report_json) {
|
|
||||||
return Report.create(report_json);
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchReport() {
|
fetchReport() {
|
||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
let payload = { data: { cache: true, facets: ["total", "prev30Days"] } };
|
let payload = this.buildPayload(["total", "prev30Days"]);
|
||||||
|
|
||||||
if (this.get("startDate")) {
|
|
||||||
payload.data.start_date = this.get("startDate").format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.get("endDate")) {
|
|
||||||
payload.data.end_date = this.get("endDate").format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.get("limit")) {
|
|
||||||
payload.data.limit = this.get("limit");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => {
|
return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => {
|
||||||
return ajax(dataSource, payload)
|
return ajax(dataSource, payload)
|
||||||
|
@ -52,17 +52,7 @@ export default Ember.Component.extend(AsyncReport, {
|
|||||||
fetchReport() {
|
fetchReport() {
|
||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
let payload = {
|
let payload = this.buildPayload(["prev_period"]);
|
||||||
data: { cache: true, facets: ["prev_period"] }
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.get("startDate")) {
|
|
||||||
payload.data.start_date = this.get("startDate").locale('en').format('YYYY-MM-DD[T]HH:mm:ss.SSSZZ');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.get("endDate")) {
|
|
||||||
payload.data.end_date = this.get("endDate").locale('en').format('YYYY-MM-DD[T]HH:mm:ss.SSSZZ');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._chart) {
|
if (this._chart) {
|
||||||
this._chart.destroy();
|
this._chart.destroy();
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import AsyncReport from "admin/mixins/async-report";
|
||||||
|
|
||||||
|
export default Ember.Component.extend(AsyncReport, {
|
||||||
|
classNames: ["dashboard-table"],
|
||||||
|
|
||||||
|
fetchReport() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
let payload = this.buildPayload(["total", "prev30Days"]);
|
||||||
|
|
||||||
|
return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => {
|
||||||
|
return ajax(dataSource, payload)
|
||||||
|
.then(response => {
|
||||||
|
this.get("reports").pushObject(this.loadReport(response.report));
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
@ -1,4 +1,5 @@
|
|||||||
import computed from "ember-addons/ember-computed-decorators";
|
import computed from "ember-addons/ember-computed-decorators";
|
||||||
|
import Report from "admin/models/report";
|
||||||
|
|
||||||
export default Ember.Mixin.create({
|
export default Ember.Mixin.create({
|
||||||
classNameBindings: ["isLoading", "dataSourceNames"],
|
classNameBindings: ["isLoading", "dataSourceNames"],
|
||||||
@ -17,6 +18,24 @@ export default Ember.Mixin.create({
|
|||||||
return dataSourceNames.split(",").map(source => `/admin/reports/${source}`);
|
return dataSourceNames.split(",").map(source => `/admin/reports/${source}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
buildPayload(facets) {
|
||||||
|
let payload = { data: { cache: true, facets } };
|
||||||
|
|
||||||
|
if (this.get("startDate")) {
|
||||||
|
payload.data.start_date = this.get("startDate").format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.get("endDate")) {
|
||||||
|
payload.data.end_date = this.get("endDate").format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.get("limit")) {
|
||||||
|
payload.data.limit = this.get("limit");
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
},
|
||||||
|
|
||||||
@computed("reports.[]", "startDate", "endDate", "dataSourceNames")
|
@computed("reports.[]", "startDate", "endDate", "dataSourceNames")
|
||||||
reportsForPeriod(reports, startDate, endDate, dataSourceNames) {
|
reportsForPeriod(reports, startDate, endDate, dataSourceNames) {
|
||||||
// on a slow network fetchReport could be called multiple times between
|
// on a slow network fetchReport could be called multiple times between
|
||||||
@ -69,7 +88,9 @@ export default Ember.Mixin.create({
|
|||||||
this.set("isLoading", false);
|
this.set("isLoading", false);
|
||||||
},
|
},
|
||||||
|
|
||||||
loadReport() {},
|
loadReport(jsonReport) {
|
||||||
|
return Report.create(jsonReport);
|
||||||
|
},
|
||||||
|
|
||||||
fetchReport() {
|
fetchReport() {
|
||||||
this.set("reports", []);
|
this.set("reports", []);
|
||||||
|
@ -1,40 +1,31 @@
|
|||||||
{{#conditional-loading-section isLoading=isLoading}}
|
{{#conditional-loading-section isLoading=isLoading}}
|
||||||
<div class="table-title">
|
<div class="table-title">
|
||||||
<h3>{{title}}</h3>
|
<h3>{{title}}</h3>
|
||||||
|
|
||||||
{{#if help}}
|
|
||||||
<a href="{{helpPage}}">{{i18n help}}</a>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#each reportsForPeriod as |report|}}
|
{{#each reportsForPeriod as |report|}}
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<table>
|
{{#unless hasBlock}}
|
||||||
<thead>
|
{{#each report.data as |data|}}
|
||||||
<tr>
|
<div class="table-cell">
|
||||||
{{#if report.labels}}
|
<span class="label">
|
||||||
{{#each report.labels as |label|}}
|
{{#if data.icon}}
|
||||||
<th>{{label}}</th>
|
{{d-icon data.icon}}
|
||||||
{{/each}}
|
{{/if}}
|
||||||
{{else}}
|
{{data.x}}:
|
||||||
{{#each report.data as |data|}}
|
</span>
|
||||||
<th>{{data.x}}</th>
|
<span class="value">
|
||||||
{{/each}}
|
{{#if data.url}}
|
||||||
{{/if}}
|
<a href="{{data.url}}">{{number data.y}}</a>
|
||||||
</tr>
|
{{else}}
|
||||||
</thead>
|
{{number data.y}}
|
||||||
<tbody>
|
{{/if}}
|
||||||
{{#unless hasBlock}}
|
</span>
|
||||||
{{#each report.data as |data|}}
|
</div>
|
||||||
<tr>
|
{{/each}}
|
||||||
<td>{{number data.y}}</td>
|
{{else}}
|
||||||
</tr>
|
{{yield (hash report=report)}}
|
||||||
{{/each}}
|
{{/unless}}
|
||||||
{{else}}
|
|
||||||
{{yield (hash report=report)}}
|
|
||||||
{{/unless}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/conditional-loading-section}}
|
{{/conditional-loading-section}}
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
{{#conditional-loading-section isLoading=isLoading}}
|
||||||
|
<div class="table-title">
|
||||||
|
<h3>{{title}}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#each reportsForPeriod as |report|}}
|
||||||
|
<div class="table-container">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{{#if report.labels}}
|
||||||
|
{{#each report.labels as |label|}}
|
||||||
|
<th>{{label}}</th>
|
||||||
|
{{/each}}
|
||||||
|
{{else}}
|
||||||
|
{{#each report.data as |data|}}
|
||||||
|
<th>{{data.x}}</th>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#unless hasBlock}}
|
||||||
|
{{#each report.data as |data|}}
|
||||||
|
<tr>
|
||||||
|
<td>{{number data.y}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
{{else}}
|
||||||
|
{{yield (hash report=report)}}
|
||||||
|
{{/unless}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{/conditional-loading-section}}
|
@ -80,17 +80,9 @@
|
|||||||
{{/conditional-loading-section}}
|
{{/conditional-loading-section}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#dashboard-inline-table dataSourceNames="users_by_type,users_by_trust_level" lastRefreshedAt=lastRefreshedAt as |context|}}
|
{{dashboard-inline-table dataSourceNames="users_by_type" lastRefreshedAt=lastRefreshedAt}}
|
||||||
<tr>
|
|
||||||
{{#each context.report.data as |data|}}
|
{{dashboard-inline-table dataSourceNames="users_by_trust_level" lastRefreshedAt=lastRefreshedAt}}
|
||||||
<td>
|
|
||||||
<a href="/admin/users/list/{{data.key}}">
|
|
||||||
{{number data.y}}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
{{/each}}
|
|
||||||
</tr>
|
|
||||||
{{/dashboard-inline-table}}
|
|
||||||
|
|
||||||
{{#conditional-loading-section isLoading=isLoading title=(i18n "admin.dashboard.backups")}}
|
{{#conditional-loading-section isLoading=isLoading title=(i18n "admin.dashboard.backups")}}
|
||||||
<div class="misc">
|
<div class="misc">
|
||||||
@ -116,7 +108,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="last-dashboard-update">
|
<div class="last-dashboard-update">
|
||||||
<div>
|
<div>
|
||||||
<h4>{{i18n "admin.dashboard.last_updated"}} </h4>
|
<h4>{{i18n "admin.dashboard.last_updated"}} </h4>
|
||||||
@ -128,58 +119,57 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{{i18n 'admin.dashboard.find_old'}} {{#link-to 'admin.dashboard'}}{{i18n "admin.dashboard.old_link"}}{{/link-to}}
|
{{i18n 'admin.dashboard.find_old'}} {{#link-to 'admin.dashboard'}}{{i18n "admin.dashboard.old_link"}}{{/link-to}}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{{/conditional-loading-section}}
|
{{/conditional-loading-section}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section-column">
|
<div class="section-column">
|
||||||
<div class="top-referred-topics">
|
<div class="top-referred-topics">
|
||||||
{{#dashboard-inline-table
|
{{#dashboard-table
|
||||||
dataSourceNames="top_referred_topics"
|
dataSourceNames="top_referred_topics"
|
||||||
lastRefreshedAt=lastRefreshedAt
|
lastRefreshedAt=lastRefreshedAt
|
||||||
limit=8
|
limit=8
|
||||||
as |context|}}
|
as |context|}}
|
||||||
{{#each context.report.data as |data|}}
|
{{#each context.report.data as |data|}}
|
||||||
<tr>
|
<tr>
|
||||||
<td class='left'>
|
<td class='left'>
|
||||||
<a href="{{data.topic_url}}">
|
<a href="{{data.topic_url}}">
|
||||||
{{data.topic_title}}
|
{{data.topic_title}}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{data.num_clicks}}
|
{{data.num_clicks}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/dashboard-inline-table}}
|
{{/dashboard-table}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="trending-search">
|
<div class="trending-search">
|
||||||
{{#dashboard-inline-table
|
{{#dashboard-table
|
||||||
limit=8
|
limit=8
|
||||||
dataSourceNames="trending_search"
|
dataSourceNames="trending_search"
|
||||||
isEnabled=logSearchQueriesEnabled
|
isEnabled=logSearchQueriesEnabled
|
||||||
disabledLabel="admin.dashboard.reports.trending_search.disabled"
|
disabledLabel="admin.dashboard.reports.trending_search.disabled"
|
||||||
startDate=lastWeek
|
startDate=lastWeek
|
||||||
endDate=endDate as |context|}}
|
endDate=endDate as |context|}}
|
||||||
{{#each context.report.data as |data|}}
|
{{#each context.report.data as |data|}}
|
||||||
<tr>
|
<tr>
|
||||||
<td class='left'>
|
<td class='left'>
|
||||||
{{data.term}}
|
{{data.term}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{number data.unique_searches}}
|
{{number data.unique_searches}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{data.ctr}}
|
{{data.ctr}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/dashboard-inline-table}}
|
{{/dashboard-table}}
|
||||||
{{{i18n "admin.dashboard.reports.trending_search.more"}}}
|
{{{i18n "admin.dashboard.reports.trending_search.more"}}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -370,6 +370,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-inline-table {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
border-bottom: 1px solid $primary-low;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
padding-bottom: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-cell {
|
||||||
|
margin-right: .5em;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-weight: 700;
|
||||||
|
margin-right: .5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dashboard-table.activity-metrics {
|
.dashboard-table.activity-metrics {
|
||||||
table {
|
table {
|
||||||
@media screen and (min-width: 400px) {
|
@media screen and (min-width: 400px) {
|
||||||
@ -381,7 +407,7 @@
|
|||||||
.d-icon {
|
.d-icon {
|
||||||
color: $primary-low-mid;
|
color: $primary-low-mid;
|
||||||
min-width: 14px;
|
min-width: 14px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@media screen and (max-width: 400px) {
|
@media screen and (max-width: 400px) {
|
||||||
.d-icon { display: none; }
|
.d-icon { display: none; }
|
||||||
|
@ -341,8 +341,11 @@ class Report
|
|||||||
|
|
||||||
def self.report_users_by_trust_level(report)
|
def self.report_users_by_trust_level(report)
|
||||||
report.data = []
|
report.data = []
|
||||||
|
|
||||||
User.real.group('trust_level').count.sort.each do |level, count|
|
User.real.group('trust_level').count.sort.each do |level, count|
|
||||||
report.data << { key: TrustLevel.levels[level.to_i], x: level.to_i, y: count }
|
key = TrustLevel.levels[level.to_i]
|
||||||
|
url = Proc.new { |key| "/admin/users/list/#{key}" }
|
||||||
|
report.data << { url: url.call(key), key: key, x: level.to_i, y: count }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -416,19 +419,20 @@ class Report
|
|||||||
def self.report_users_by_type(report)
|
def self.report_users_by_type(report)
|
||||||
report.data = []
|
report.data = []
|
||||||
|
|
||||||
label = Proc.new { |key| I18n.t("reports.users_by_type.xaxis_labels.#{key}") }
|
label = Proc.new { |x| I18n.t("reports.users_by_type.xaxis_labels.#{x}") }
|
||||||
|
url = Proc.new { |key| "/admin/users/list/#{key}" }
|
||||||
|
|
||||||
admins = User.real.admins.count
|
admins = User.real.admins.count
|
||||||
report.data << { key: "admins", x: label.call("admin"), y: admins } if admins > 0
|
report.data << { url: url.call("admins"), icon: "shield", key: "admins", x: label.call("admin"), y: admins } if admins > 0
|
||||||
|
|
||||||
moderators = User.real.moderators.count
|
moderators = User.real.moderators.count
|
||||||
report.data << { key: "moderators", x: label.call("moderator"), y: moderators } if moderators > 0
|
report.data << { url: url.call("moderators"), icon: "shield", key: "moderators", x: label.call("moderator"), y: moderators } if moderators > 0
|
||||||
|
|
||||||
suspended = User.real.suspended.count
|
suspended = User.real.suspended.count
|
||||||
report.data << { key: "suspended", x: label.call("suspended"), y: suspended } if suspended > 0
|
report.data << { url: url.call("suspended"), icon: "ban", key: "suspended", x: label.call("suspended"), y: suspended } if suspended > 0
|
||||||
|
|
||||||
silenced = User.real.silenced.count
|
silenced = User.real.silenced.count
|
||||||
report.data << { key: "silenced", x: label.call("silenced"), y: silenced } if silenced > 0
|
report.data << { url: url.call("silenced"), icon: "ban", key: "silenced", x: label.call("silenced"), y: silenced } if silenced > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.report_top_referred_topics(report)
|
def self.report_top_referred_topics(report)
|
||||||
|
Reference in New Issue
Block a user