UX: tooltips and improvements to new dashboard
- tooltips - revert chart title UI - reduce period chooser font-size - localize dates of data points - fix a bug where multiple reports were loaded at the same time - fix a bug where % was not showing anymore - remove spacing at the top - remove loadingTitle feature (Loading...%report name%) incompatible with new hijack design
This commit is contained in:
parent
131b7f5da5
commit
9554d9c56a
app/assets
javascripts
admin
components
controllers
mixins
templates/components
discourse
stylesheets/common
test/javascripts/fixtures
|
@ -6,8 +6,6 @@ export default Ember.Component.extend(AsyncReport, {
|
||||||
classNames: ["dashboard-table", "dashboard-inline-table", "fixed"],
|
classNames: ["dashboard-table", "dashboard-inline-table", "fixed"],
|
||||||
help: null,
|
help: null,
|
||||||
helpPage: null,
|
helpPage: null,
|
||||||
title: null,
|
|
||||||
loadingTitle: null,
|
|
||||||
|
|
||||||
loadReport(report_json) {
|
loadReport(report_json) {
|
||||||
return Report.create(report_json);
|
return Report.create(report_json);
|
||||||
|
@ -30,12 +28,10 @@ export default Ember.Component.extend(AsyncReport, {
|
||||||
payload.data.limit = this.get("limit");
|
payload.data.limit = this.get("limit");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.set("reports", Ember.Object.create());
|
|
||||||
|
|
||||||
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)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.set(`reports.${response.report.report_key}`, this.loadReport(response.report));
|
this.get("reports").pushObject(this.loadReport(response.report));
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import AsyncReport from "admin/mixins/async-report";
|
||||||
import Report from "admin/models/report";
|
import Report from "admin/models/report";
|
||||||
import { number } from 'discourse/lib/formatter';
|
import { number } from 'discourse/lib/formatter';
|
||||||
import loadScript from "discourse/lib/load-script";
|
import loadScript from "discourse/lib/load-script";
|
||||||
|
import { registerTooltip, unregisterTooltip } from "discourse/lib/tooltip";
|
||||||
|
|
||||||
function collapseWeekly(data, average) {
|
function collapseWeekly(data, average) {
|
||||||
let aggregate = [];
|
let aggregate = [];
|
||||||
|
@ -25,7 +26,7 @@ function collapseWeekly(data, average) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Ember.Component.extend(AsyncReport, {
|
export default Ember.Component.extend(AsyncReport, {
|
||||||
classNames: ["dashboard-mini-chart"],
|
classNames: ["chart", "dashboard-mini-chart"],
|
||||||
total: 0,
|
total: 0,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
@ -34,6 +35,18 @@ export default Ember.Component.extend(AsyncReport, {
|
||||||
this._colorsPool = ["rgb(0,136,204)", "rgb(235,83,148)"];
|
this._colorsPool = ["rgb(0,136,204)", "rgb(235,83,148)"];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
didRender() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
registerTooltip($(this.element).find("[data-tooltip]"));
|
||||||
|
},
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
unregisterTooltip($(this.element).find("[data-tooltip]"));
|
||||||
|
},
|
||||||
|
|
||||||
pickColorAtIndex(index) {
|
pickColorAtIndex(index) {
|
||||||
return this._colorsPool[index] || this._colorsPool[0];
|
return this._colorsPool[index] || this._colorsPool[0];
|
||||||
},
|
},
|
||||||
|
@ -58,12 +71,10 @@ export default Ember.Component.extend(AsyncReport, {
|
||||||
this._chart = null;
|
this._chart = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.set("reports", Ember.Object.create());
|
|
||||||
|
|
||||||
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)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.set(`reports.${response.report.report_key}`, this.loadReport(response.report));
|
this.get("reports").pushObject(this.loadReport(response.report));
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
@ -93,13 +104,13 @@ export default Ember.Component.extend(AsyncReport, {
|
||||||
if (!$chartCanvas.length) return;
|
if (!$chartCanvas.length) return;
|
||||||
const context = $chartCanvas[0].getContext("2d");
|
const context = $chartCanvas[0].getContext("2d");
|
||||||
|
|
||||||
const reports = _.values(this.get("reports"));
|
const reportsForPeriod = this.get("reportsForPeriod");
|
||||||
|
|
||||||
const labels = Ember.makeArray(reports.get("firstObject.data")).map(d => d.x);
|
const labels = Ember.makeArray(reportsForPeriod.get("firstObject.data")).map(d => d.x);
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
labels,
|
labels,
|
||||||
datasets: reports.map(report => {
|
datasets: reportsForPeriod.map(report => {
|
||||||
return {
|
return {
|
||||||
data: Ember.makeArray(report.data).map(d => d.y),
|
data: Ember.makeArray(report.data).map(d => d.y),
|
||||||
backgroundColor: "rgba(200,220,240,0.3)",
|
backgroundColor: "rgba(200,220,240,0.3)",
|
||||||
|
@ -127,6 +138,11 @@ export default Ember.Component.extend(AsyncReport, {
|
||||||
type: "line",
|
type: "line",
|
||||||
data,
|
data,
|
||||||
options: {
|
options: {
|
||||||
|
tooltips: {
|
||||||
|
callbacks: {
|
||||||
|
title: (context) => moment(context[0].xLabel, "YYYY-MM-DD").format("LL")
|
||||||
|
}
|
||||||
|
},
|
||||||
legend: {
|
legend: {
|
||||||
display: false
|
display: false
|
||||||
},
|
},
|
||||||
|
|
|
@ -100,7 +100,7 @@ export default Ember.Controller.extend({
|
||||||
return fullDay.subtract(1, "month").startOf("day");
|
return fullDay.subtract(1, "month").startOf("day");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return null;
|
return fullDay.subtract(1, "month").startOf("day");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,14 @@ import computed from "ember-addons/ember-computed-decorators";
|
||||||
|
|
||||||
export default Ember.Mixin.create({
|
export default Ember.Mixin.create({
|
||||||
classNameBindings: ["isLoading"],
|
classNameBindings: ["isLoading"],
|
||||||
|
|
||||||
reports: null,
|
reports: null,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
dataSourceNames: "",
|
dataSourceNames: "",
|
||||||
|
title: null,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super();
|
this._super();
|
||||||
this.set("reports", Ember.Object.create());
|
this.set("reports", []);
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("dataSourceNames")
|
@computed("dataSourceNames")
|
||||||
|
@ -17,8 +17,27 @@ export default Ember.Mixin.create({
|
||||||
return dataSourceNames.split(",").map(source => `/admin/reports/${source}`);
|
return dataSourceNames.split(",").map(source => `/admin/reports/${source}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@computed("reports.[]", "startDate", "endDate")
|
||||||
|
reportsForPeriod(reports, startDate, endDate) {
|
||||||
|
// on a slow network fetchReport could be called multiple times between
|
||||||
|
// T and T+x, and all the ajax responses would occur after T+(x+y)
|
||||||
|
// to avoid any inconsistencies we filter by period and make sure
|
||||||
|
// the array contains only unique values
|
||||||
|
reports = reports.uniqBy("report_key");
|
||||||
|
|
||||||
|
if (!startDate || !endDate) {
|
||||||
|
return reports;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reports.filter(report => {
|
||||||
|
return report.report_key.includes(startDate.format("YYYYMMDD")) &&
|
||||||
|
report.report_key.includes(endDate.format("YYYYMMDD"));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
this.fetchReport()
|
this.fetchReport()
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.renderReport();
|
this.renderReport();
|
||||||
|
@ -27,6 +46,7 @@ export default Ember.Mixin.create({
|
||||||
|
|
||||||
didUpdateAttrs() {
|
didUpdateAttrs() {
|
||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
this.fetchReport()
|
this.fetchReport()
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.renderReport();
|
this.renderReport();
|
||||||
|
@ -35,26 +55,14 @@ export default Ember.Mixin.create({
|
||||||
|
|
||||||
renderReport() {
|
renderReport() {
|
||||||
if (!this.element || this.isDestroying || this.isDestroyed) return;
|
if (!this.element || this.isDestroying || this.isDestroyed) return;
|
||||||
|
this.set("title", this.get("reportsForPeriod").map(r => r.title).join(", "));
|
||||||
const reports = _.values(this.get("reports"));
|
this.set("isLoading", false);
|
||||||
|
|
||||||
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() {},
|
loadReport() {},
|
||||||
|
|
||||||
fetchReport() {
|
fetchReport() {
|
||||||
|
this.set("reports", []);
|
||||||
this.set("isLoading", true);
|
this.set("isLoading", true);
|
||||||
this.set("loadingTitle", I18n.t("conditional_loading_section.loading"));
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{#conditional-loading-section isLoading=isLoading title=loadingTitle}}
|
{{#conditional-loading-section isLoading=isLoading}}
|
||||||
<div class="table-title">
|
<div class="table-title">
|
||||||
<h3>{{title}}</h3>
|
<h3>{{title}}</h3>
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#each-in reports as |key report|}}
|
{{#each reportsForPeriod as |report|}}
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -36,5 +36,5 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{{/each-in}}
|
{{/each}}
|
||||||
{{/conditional-loading-section}}
|
{{/conditional-loading-section}}
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
{{#conditional-loading-section isLoading=isLoading title=loadingTitle}}
|
{{#conditional-loading-section isLoading=isLoading}}
|
||||||
<div class="dashboard-mini-statuses">
|
{{#each reportsForPeriod as |report|}}
|
||||||
{{#each-in reports as |key report|}}
|
<div class="status">
|
||||||
<div class="dashboard-mini-status" title="{{report.trendTitle}}">
|
<h4 class="title">
|
||||||
<span class="indicator" style="background-color:{{report.color}}"></span>
|
<a href="{{report.reportUrl}}">
|
||||||
<div class="legend">
|
{{report.title}}
|
||||||
<h4 class="title">
|
</a>
|
||||||
<a href="{{report.reportUrl}}">
|
|
||||||
{{report.title}}
|
|
||||||
</a>
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<div class="trend">
|
<span class="info" data-tooltip="{{report.description}}">
|
||||||
<span class="value" title="{{report.trendTitle}}">
|
{{d-icon "question-circle"}}
|
||||||
{{#if report.average}}
|
</span>
|
||||||
{{report.currentAverage}}
|
</h4>
|
||||||
{{else}}
|
|
||||||
{{number report.currentTotal noTitle="true"}}
|
<div class="trend {{report.trend}}">
|
||||||
{{/if}}
|
<span class="trend-value" title="{{report.trendTitle}}">
|
||||||
</span>
|
{{#if report.average}}
|
||||||
{{#if report.trendIcon}}
|
{{number report.currentAverage}}{{#if report.percent}}%{{/if}}
|
||||||
{{d-icon report.trendIcon}}
|
{{else}}
|
||||||
{{/if}}
|
{{number report.currentTotal noTitle="true"}}{{#if report.percent}}%{{/if}}
|
||||||
</div>
|
{{/if}}
|
||||||
</div>
|
</span>
|
||||||
|
|
||||||
|
{{#if report.trendIcon}}
|
||||||
|
{{d-icon report.trendIcon class="trend-icon"}}
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/each-in}}
|
</div>
|
||||||
</div>
|
{{/each}}
|
||||||
|
|
||||||
<div class="chart-canvas-container">
|
<div class="chart-canvas-container">
|
||||||
<canvas class="chart-canvas"></canvas>
|
<canvas class="chart-canvas"></canvas>
|
||||||
|
|
|
@ -3,5 +3,7 @@ export default Ember.Component.extend({
|
||||||
|
|
||||||
classNameBindings: ["isLoading"],
|
classNameBindings: ["isLoading"],
|
||||||
|
|
||||||
isLoading: false
|
isLoading: false,
|
||||||
|
|
||||||
|
title: I18n.t("conditional_loading_section.loading")
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
export function showTooltip() {
|
||||||
|
const fadeSpeed = 300;
|
||||||
|
const tooltipID = "#discourse-tooltip";
|
||||||
|
const $this = $(this);
|
||||||
|
const $parent = $this.offsetParent();
|
||||||
|
const content = $this.attr("data-tooltip");
|
||||||
|
const retina = window.devicePixelRatio && window.devicePixelRatio > 1 ? "class='retina'" : "";
|
||||||
|
|
||||||
|
let pos = $this.offset();
|
||||||
|
const delta = $parent.offset();
|
||||||
|
pos.top -= delta.top;
|
||||||
|
pos.left -= delta.left;
|
||||||
|
|
||||||
|
$(tooltipID).fadeOut(fadeSpeed).remove();
|
||||||
|
|
||||||
|
$(this).after(`
|
||||||
|
<div id="discourse-tooltip" ${retina}>
|
||||||
|
<div class="tooltip-pointer"></div>
|
||||||
|
<div class="tooltip-content">${content}</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
$(window).on("click.discourse", (event) => {
|
||||||
|
if ($(event.target).closest(tooltipID).length === 0) {
|
||||||
|
$(tooltipID).remove();
|
||||||
|
$(window).off("click.discourse");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const $tooltip = $(tooltipID);
|
||||||
|
$tooltip.css({top: 0, left: 0});
|
||||||
|
|
||||||
|
let left = (pos.left - ($tooltip.width() / 2) + $this.width()/2);
|
||||||
|
if (left < 0) {
|
||||||
|
$tooltip.find(".tooltip-pointer").css({
|
||||||
|
"margin-left": left*2 + "px"
|
||||||
|
});
|
||||||
|
left = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// also do a right margin fix
|
||||||
|
const parentWidth = $parent.width();
|
||||||
|
if (left + $tooltip.width() > parentWidth) {
|
||||||
|
let oldLeft = left;
|
||||||
|
left = parentWidth - $tooltip.width();
|
||||||
|
|
||||||
|
$tooltip.find(".tooltip-pointer").css({
|
||||||
|
"margin-left": (oldLeft - left) * 2 + "px"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$tooltip.css({
|
||||||
|
top: pos.top + 5 + "px",
|
||||||
|
left: left + "px"
|
||||||
|
});
|
||||||
|
|
||||||
|
$tooltip.fadeIn(fadeSpeed);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerTooltip(jqueryContext) {
|
||||||
|
if (jqueryContext.length) {
|
||||||
|
jqueryContext.on('click', showTooltip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unregisterTooltip(jqueryContext) {
|
||||||
|
if (jqueryContext.length) {
|
||||||
|
jqueryContext.off('click');
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,12 @@
|
||||||
.dashboard-next {
|
.dashboard-next {
|
||||||
|
|
||||||
&.admin-contents {
|
&.admin-contents {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section-top {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.section-columns {
|
.section-columns {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@ -62,223 +65,26 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-table {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
|
|
||||||
&.is-disabled {
|
|
||||||
background: $primary-low;
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include small-width {
|
|
||||||
table {
|
|
||||||
tbody tr td {
|
|
||||||
font-size: $font-down-2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-loading {
|
|
||||||
height: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-title {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: .5em 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
table-layout: fixed;
|
|
||||||
|
|
||||||
thead {
|
|
||||||
border: 1px solid $primary-low;
|
|
||||||
tr {
|
|
||||||
background: $primary-low;
|
|
||||||
th {
|
|
||||||
text-align: center;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody {
|
|
||||||
tr {
|
|
||||||
td:first-child {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
border: 1px solid $primary-low;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
td.left {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.value {
|
|
||||||
i {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.high-trending-up, &.trending-up {
|
|
||||||
i.up {
|
|
||||||
color: $success;
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.high-trending-down, &.trending-down {
|
|
||||||
i.down {
|
|
||||||
color: $danger;
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.no-change {
|
|
||||||
i.down {
|
|
||||||
display: inline;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.charts {
|
.charts {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
.dashboard-mini-statuses {
|
.chart {
|
||||||
margin-bottom: 1em;
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-mini-status {
|
|
||||||
flex-direction: row;
|
|
||||||
margin-right: 1em;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.indicator {
|
|
||||||
margin-right: .5em;
|
|
||||||
width: .33em;
|
|
||||||
height: 35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.legend {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
a {color: black;}
|
|
||||||
|
|
||||||
font-size: $font-down-2;
|
|
||||||
font-weight: 700;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trend {
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
.d-icon {
|
|
||||||
font-weight: 700;
|
|
||||||
|
|
||||||
&.d-icon-angle-down, &.d-icon-angle-double-down {
|
|
||||||
color: $danger;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.d-icon-angle-up, &.d-icon-angle-double-up {
|
|
||||||
color: rgb(17, 141, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-mini-chart {
|
|
||||||
max-width: calc(100% * 1/3);
|
max-width: calc(100% * 1/3);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-basis: 100%;
|
flex-basis: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
|
||||||
.conditional-loading-section {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include small-width {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-loading {
|
|
||||||
height: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-container.visible {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d-icon-question-circle {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-title {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 1em 0;
|
|
||||||
a, a:visited {
|
|
||||||
color: $primary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.high-trending-up, &.trending-up {
|
|
||||||
.chart-trend, .data-point {
|
|
||||||
color: rgb(17, 141, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.high-trending-down, &.trending-down {
|
|
||||||
.chart-trend, .data-point {
|
|
||||||
color: $danger;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@include small-width {
|
@include small-width {
|
||||||
.dashboard-mini-chart {
|
.chart {
|
||||||
width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-trend {
|
|
||||||
font-size: $font-up-3;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-canvas-container {
|
.chart-canvas-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0 1em 0 0;
|
padding: 0 1em 0 0;
|
||||||
|
@ -300,4 +106,204 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.community-health {
|
||||||
|
.period-chooser .period-chooser-header {
|
||||||
|
.selected-name, .d-icon {
|
||||||
|
font-size: $font-up-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-icon {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.dashboard-mini-chart {
|
||||||
|
.status {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: $font-0;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
a { color: $primary; }
|
||||||
|
|
||||||
|
.info {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: .25em;
|
||||||
|
color: $primary-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend {
|
||||||
|
margin-right: 1em;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.trending-down, &.high-trending-down {
|
||||||
|
color: $danger;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.trending-up, &.high-trending-up {
|
||||||
|
color: $success;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-value {
|
||||||
|
font-size: $font-up-1;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-icon {
|
||||||
|
font-size: $font-up-1;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.conditional-loading-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include small-width {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-loading {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container.visible {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-icon-question-circle {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-title {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 1em 0;
|
||||||
|
a, a:visited {
|
||||||
|
color: $primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.high-trending-up, &.trending-up {
|
||||||
|
.chart-trend, .data-point {
|
||||||
|
color: $success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.high-trending-down, &.trending-down {
|
||||||
|
.chart-trend, .data-point {
|
||||||
|
color: $danger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-table {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
background: $primary-low;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include small-width {
|
||||||
|
table {
|
||||||
|
tbody tr td {
|
||||||
|
font-size: $font-down-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-loading {
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: .5em 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
table-layout: fixed;
|
||||||
|
|
||||||
|
thead {
|
||||||
|
border: 1px solid $primary-low;
|
||||||
|
tr {
|
||||||
|
background: $primary-low;
|
||||||
|
th {
|
||||||
|
text-align: center;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td:first-child {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
border: 1px solid $primary-low;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
td.left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.value {
|
||||||
|
i {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.high-trending-up, &.trending-up {
|
||||||
|
i.up {
|
||||||
|
color: $success;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.high-trending-down, &.trending-down {
|
||||||
|
i.down {
|
||||||
|
color: $danger;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.no-change {
|
||||||
|
i.down {
|
||||||
|
display: inline;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
$discourse-tooltip-background: $secondary;
|
||||||
|
$discourse-tooltip-border: $primary-medium;
|
||||||
|
|
||||||
|
#discourse-tooltip {
|
||||||
|
background-color: $discourse-tooltip-background;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1000;
|
||||||
|
border: 1px solid $discourse-tooltip-border;
|
||||||
|
max-width: 400px;
|
||||||
|
margin-top: 25px;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
display: none;
|
||||||
|
font-size: $font-0;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&.retina {
|
||||||
|
border: 0.5px solid $discourse-tooltip-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-pointer {
|
||||||
|
position: relative;
|
||||||
|
background: $discourse-tooltip-background;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-pointer:before, .tooltip-pointer:after {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
border: solid transparent;
|
||||||
|
bottom: 100%;
|
||||||
|
content: "";
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-pointer:after
|
||||||
|
{
|
||||||
|
border-bottom-color: $discourse-tooltip-background;
|
||||||
|
border-width: 8px;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -8px;
|
||||||
|
margin-bottom: -0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-pointer:before {
|
||||||
|
border-bottom-color: $discourse-tooltip-border;
|
||||||
|
border-width: 9px;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -9px;
|
||||||
|
margin-bottom: -0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-content {
|
||||||
|
padding: 0 0.5em;
|
||||||
|
font-size: $font-down-1;
|
||||||
|
color: $primary-medium;
|
||||||
|
line-height: 1.4em;
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,8 @@ export default {
|
||||||
"category_id": null,
|
"category_id": null,
|
||||||
"group_id": null,
|
"group_id": null,
|
||||||
"prev30Days": null,
|
"prev30Days": null,
|
||||||
"labels": null
|
"labels": null,
|
||||||
|
"report_key": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,8 @@ export default {
|
||||||
"category_id": null,
|
"category_id": null,
|
||||||
"group_id": null,
|
"group_id": null,
|
||||||
"prev30Days": 46,
|
"prev30Days": 46,
|
||||||
"labels": null
|
"labels": null,
|
||||||
|
"report_key": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -42,7 +42,8 @@ export default {
|
||||||
"category_id": null,
|
"category_id": null,
|
||||||
"group_id": null,
|
"group_id": null,
|
||||||
"prev30Days": null,
|
"prev30Days": null,
|
||||||
"labels": null
|
"labels": null,
|
||||||
|
"report_key": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,8 @@ export default {
|
||||||
"category_id": null,
|
"category_id": null,
|
||||||
"group_id": null,
|
"group_id": null,
|
||||||
"prev30Days": 0,
|
"prev30Days": 0,
|
||||||
"labels": null
|
"labels": null,
|
||||||
|
"report_key": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,7 +39,8 @@ export default {
|
||||||
"category_id": null,
|
"category_id": null,
|
||||||
"group_id": null,
|
"group_id": null,
|
||||||
"prev30Days": 0,
|
"prev30Days": 0,
|
||||||
"labels": null
|
"labels": null,
|
||||||
|
"report_key": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,8 @@ export default {
|
||||||
"category_id": null,
|
"category_id": null,
|
||||||
"group_id": null,
|
"group_id": null,
|
||||||
"prev30Days": 0,
|
"prev30Days": 0,
|
||||||
"labels": null
|
"labels": null,
|
||||||
|
"report_key": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,8 @@ export default {
|
||||||
"category_id": null,
|
"category_id": null,
|
||||||
"group_id": null,
|
"group_id": null,
|
||||||
"prev30Days": null,
|
"prev30Days": null,
|
||||||
"labels": ["Term", "Searches", "Unique"]
|
"labels": ["Term", "Searches", "Unique"],
|
||||||
|
"report_key": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,8 @@ export default {
|
||||||
"category_id": null,
|
"category_id": null,
|
||||||
"group_id": null,
|
"group_id": null,
|
||||||
"prev30Days": null,
|
"prev30Days": null,
|
||||||
"labels": null
|
"labels": null,
|
||||||
|
"report_key": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,8 @@ export default {
|
||||||
"category_id": null,
|
"category_id": null,
|
||||||
"group_id": null,
|
"group_id": null,
|
||||||
"prev30Days": null,
|
"prev30Days": null,
|
||||||
"labels": null
|
"labels": null,
|
||||||
|
"report_key": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue