FIX: improves reports resilience (#6239)
This commit makes most of the reports now lazy loaded, and making them benefits from graceful failures.
This commit is contained in:
parent
ffc8c52bf5
commit
7f2f3b8b22
|
@ -0,0 +1,3 @@
|
|||
export default Ember.Component.extend({
|
||||
classNames: ["admin-report-counters"]
|
||||
});
|
|
@ -4,18 +4,11 @@ import AdminDashboardNext from "admin/models/admin-dashboard-next";
|
|||
import Report from "admin/models/report";
|
||||
import PeriodComputationMixin from "admin/mixins/period-computation";
|
||||
|
||||
const ACTIVITY_METRICS_REPORTS = [
|
||||
"page_view_total_reqs",
|
||||
"visits",
|
||||
"time_to_first_response",
|
||||
"likes",
|
||||
"flags",
|
||||
"user_to_user_private_messages_with_replies"
|
||||
];
|
||||
|
||||
function staticReport(reportType) {
|
||||
return function() {
|
||||
return this.get("reports").find(x => x.type === reportType);
|
||||
return Ember.makeArray(this.get("reports")).find(
|
||||
report => report.type === reportType
|
||||
);
|
||||
}.property("reports.[]");
|
||||
}
|
||||
|
||||
|
@ -35,6 +28,18 @@ export default Ember.Controller.extend(PeriodComputationMixin, {
|
|||
return { table: { total: false, limit: 8 } };
|
||||
},
|
||||
|
||||
@computed
|
||||
activityMetrics() {
|
||||
return [
|
||||
"page_view_total_reqs",
|
||||
"visits",
|
||||
"time_to_first_response",
|
||||
"likes",
|
||||
"flags",
|
||||
"user_to_user_private_messages_with_replies"
|
||||
];
|
||||
},
|
||||
|
||||
@computed
|
||||
trendingSearchOptions() {
|
||||
return { table: { total: false, limit: 8 } };
|
||||
|
@ -43,13 +48,6 @@ export default Ember.Controller.extend(PeriodComputationMixin, {
|
|||
usersByTypeReport: staticReport("users_by_type"),
|
||||
usersByTrustLevelReport: staticReport("users_by_trust_level"),
|
||||
|
||||
@computed("reports.[]")
|
||||
activityMetricsReports(reports) {
|
||||
return reports.filter(report =>
|
||||
ACTIVITY_METRICS_REPORTS.includes(report.type)
|
||||
);
|
||||
},
|
||||
|
||||
fetchDashboard() {
|
||||
if (this.get("isLoading")) return;
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<div class="cell title">
|
||||
{{#if model.icon}}
|
||||
{{d-icon model.icon}}
|
||||
{{/if}}
|
||||
<a href="{{model.reportUrl}}">{{model.title}}</a>
|
||||
</div>
|
||||
|
||||
<div class="cell value today-count">{{number model.todayCount}}</div>
|
||||
|
||||
<div class="cell value yesterday-count {{model.yesterdayTrend}}" title={{model.yesterdayCountTitle}}>
|
||||
{{number model.yesterdayCount}} {{d-icon model.yesterdayTrendIcon}}
|
||||
</div>
|
||||
|
||||
<div class="cell value sevendays-count {{model.sevenDaysTrend}}" title={{model.sevenDaysCountTitle}}>
|
||||
{{number model.lastSevenDaysCount}} {{d-icon model.sevenDaysTrendIcon}}
|
||||
</div>
|
||||
|
||||
<div class="cell value thirty-days-count {{model.thirtyDaysTrend}}" title={{model.thirtyDaysCountTitle}}>
|
||||
{{number model.lastThirtyDaysCount}} {{d-icon model.thirtyDaysTrendIcon}}
|
||||
</div>
|
|
@ -68,7 +68,7 @@
|
|||
{{else}}
|
||||
<div class="alert alert-info no-data">
|
||||
{{d-icon "pie-chart"}}
|
||||
<span>{{i18n 'admin.dashboard.reports.no_data'}}</span>
|
||||
<span>{{i18n "admin.dashboard.reports.no_data"}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
|
|
|
@ -57,61 +57,50 @@
|
|||
<div class="section-columns">
|
||||
<div class="section-column">
|
||||
<div class="admin-report activity-metrics">
|
||||
{{#conditional-loading-section isLoading=isLoading title=(i18n "admin.dashboard.activity_metrics")}}
|
||||
<div class="report-header">
|
||||
<div class="report-title">
|
||||
<h3 class="title">
|
||||
{{#link-to "adminReports" class="report-link"}}
|
||||
{{i18n "admin.dashboard.activity_metrics"}}
|
||||
{{/link-to}}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="report-header">
|
||||
<div class="report-title">
|
||||
<h3 class="title">
|
||||
{{#link-to "adminReports" class="report-link"}}
|
||||
{{i18n "admin.dashboard.activity_metrics"}}
|
||||
{{/link-to}}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="report-body">
|
||||
<div class="admin-report-counters-list">
|
||||
<div class="counters-header">
|
||||
<div class="header"></div>
|
||||
<div class="header">{{i18n 'admin.dashboard.reports.today'}}</div>
|
||||
<div class="header">{{i18n 'admin.dashboard.reports.yesterday'}}</div>
|
||||
<div class="header">{{i18n 'admin.dashboard.reports.last_7_days'}}</div>
|
||||
<div class="header">{{i18n 'admin.dashboard.reports.last_30_days'}}</div>
|
||||
</div>
|
||||
|
||||
<div class="report-body">
|
||||
<div class="admin-report-table">
|
||||
<table class="report-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="admin-report-table-header"></th>
|
||||
<th class="admin-report-table-header">
|
||||
{{i18n 'admin.dashboard.reports.today'}}
|
||||
</th>
|
||||
<th class="admin-report-table-header">
|
||||
{{i18n 'admin.dashboard.reports.yesterday'}}
|
||||
</th>
|
||||
<th class="admin-report-table-header">
|
||||
{{i18n 'admin.dashboard.reports.last_7_days'}}
|
||||
</th>
|
||||
<th class="admin-report-table-header">
|
||||
{{i18n 'admin.dashboard.reports.last_30_days'}}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each activityMetricsReports as |report|}}
|
||||
{{admin-report-counts report=report allTime=false class="admin-report-table-row"}}
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{#each activityMetrics as |metric|}}
|
||||
{{admin-report
|
||||
showHeader=false
|
||||
forcedModes="counters"
|
||||
dataSourceName=metric}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/conditional-loading-section}}
|
||||
</div>
|
||||
</div>
|
||||
{{#link-to "adminReports"}}
|
||||
{{i18n "admin.dashboard.all_reports"}}
|
||||
{{/link-to}}
|
||||
|
||||
<div class="user-metrics">
|
||||
{{admin-report
|
||||
forcedModes="inline-table"
|
||||
report=usersByTypeReport
|
||||
lastRefreshedAt=lastRefreshedAt}}
|
||||
{{#conditional-loading-section isLoading=isLoading}}
|
||||
{{admin-report
|
||||
forcedModes="inline-table"
|
||||
report=usersByTypeReport
|
||||
lastRefreshedAt=lastRefreshedAt}}
|
||||
|
||||
{{admin-report
|
||||
forcedModes="inline-table"
|
||||
report=usersByTrustLevelReport
|
||||
lastRefreshedAt=lastRefreshedAt}}
|
||||
{{admin-report
|
||||
forcedModes="inline-table"
|
||||
report=usersByTrustLevelReport
|
||||
lastRefreshedAt=lastRefreshedAt}}
|
||||
{{/conditional-loading-section}}
|
||||
</div>
|
||||
|
||||
{{#conditional-loading-section isLoading=isLoading title=(i18n "admin.dashboard.backups")}}
|
||||
|
|
|
@ -951,6 +951,7 @@ table#user-badges {
|
|||
@import "common/admin/plugins";
|
||||
@import "common/admin/admin_reports";
|
||||
@import "common/admin/admin_report";
|
||||
@import "common/admin/admin_report_counters";
|
||||
@import "common/admin/admin_report_chart";
|
||||
@import "common/admin/admin_report_table";
|
||||
@import "common/admin/admin_report_inline_table";
|
||||
|
|
|
@ -27,9 +27,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
.conditional-loading-section.is-loading {
|
||||
.conditional-loading-section {
|
||||
flex: 1;
|
||||
margin: 0.5em 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.report-header {
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
.admin-report-counters-list {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
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;
|
||||
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 {
|
||||
&:last-child .admin-report-counters {
|
||||
border-bottom: 1px solid $primary-low;
|
||||
}
|
||||
|
||||
.admin-report-counters {
|
||||
display: grid;
|
||||
flex: 1;
|
||||
grid-template-columns: 33% repeat(auto-fit, minmax(20px, 1fr));
|
||||
grid-template-rows: repeat(auto-fit, minmax(32px, 1fr));
|
||||
border: 1px solid $primary-low;
|
||||
align-items: center;
|
||||
border-bottom: 0;
|
||||
|
||||
.cell {
|
||||
padding: 0.25em;
|
||||
text-align: right;
|
||||
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 {
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
|
||||
.d-icon {
|
||||
color: $primary-low-mid;
|
||||
min-width: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
i {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 400px) {
|
||||
&.title {
|
||||
padding: 8px 0 8px 4px;
|
||||
font-size: $font-down-1;
|
||||
|
||||
.d-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.high-trending-up,
|
||||
&.trending-up {
|
||||
i {
|
||||
color: $success;
|
||||
}
|
||||
}
|
||||
&.high-trending-down,
|
||||
&.trending-down {
|
||||
i {
|
||||
color: $danger;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-data {
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: $font-0;
|
||||
border: 0;
|
||||
background: $primary-low;
|
||||
color: $primary-medium;
|
||||
.d-icon {
|
||||
font-size: $font-up-1;
|
||||
margin: 0 0.25em 0 0;
|
||||
color: $primary-low-mid;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -262,6 +262,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.activity-metrics {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.user-metrics {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
@ -330,85 +334,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.admin-report.activity-metrics {
|
||||
.report-table {
|
||||
@media screen and (min-width: 400px) {
|
||||
table-layout: auto;
|
||||
}
|
||||
tr th {
|
||||
text-align: right;
|
||||
}
|
||||
.d-icon {
|
||||
color: $primary-low-mid;
|
||||
min-width: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
@media screen and (max-width: 400px) {
|
||||
.d-icon {
|
||||
display: none;
|
||||
}
|
||||
td.title {
|
||||
padding: 8px 0 8px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
td {
|
||||
text-overflow: unset;
|
||||
overflow: auto;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
td.left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td.title {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td.value {
|
||||
text-align: right;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
&.high-trending-up,
|
||||
&.trending-up {
|
||||
i {
|
||||
color: $success;
|
||||
}
|
||||
}
|
||||
&.high-trending-down,
|
||||
&.trending-down {
|
||||
i {
|
||||
color: $danger;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rtl .dashboard-next {
|
||||
.section-column {
|
||||
&:last-child {
|
||||
|
@ -422,17 +347,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.dashboard-table table tbody tr {
|
||||
td.title {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
td.value i {
|
||||
margin-right: 0;
|
||||
margin-left: -12px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-metrics .table-cell {
|
||||
margin: 0 0 5px 10px;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,7 @@ class Admin::DashboardNextController < Admin::AdminController
|
|||
render json: data
|
||||
end
|
||||
|
||||
def moderation
|
||||
end
|
||||
def moderation; end
|
||||
|
||||
def general
|
||||
data = AdminDashboardNextGeneralData.fetch_cached_stats
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
class AdminDashboardNextGeneralData < AdminDashboardNextData
|
||||
def reports
|
||||
@reports ||= %w{
|
||||
page_view_total_reqs
|
||||
visits
|
||||
time_to_first_response
|
||||
likes
|
||||
flags
|
||||
user_to_user_private_messages_with_replies
|
||||
users_by_type
|
||||
users_by_trust_level
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ class Report
|
|||
group_filtering: self.group_filtering,
|
||||
modes: self.modes,
|
||||
}.tap do |json|
|
||||
json[:icon] = self.icon if self.icon
|
||||
json[:error] = self.error if self.error
|
||||
json[:total] = self.total if self.total
|
||||
json[:prev_period] = self.prev_period if self.prev_period
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
"/admin/reports/flags": {
|
||||
report: {
|
||||
report_key: "flags"
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
"/admin/reports/likes": {
|
||||
report: {
|
||||
report_key: "likes"
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
"/admin/reports/page_view_total_reqs": {
|
||||
report: {
|
||||
report_key: "page_view_total_reqs"
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
"/admin/reports/time_to_first_response": {
|
||||
report: {
|
||||
report_key: "time_to_first_response"
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
"/admin/reports/user_to_user_private_messages_with_replies": {
|
||||
report: {
|
||||
report_key: "user_to_user_private_messages_with_replies"
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
"/admin/reports/visits": {
|
||||
report: {
|
||||
report_key: "posts"
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue