From 1dccc163e4edef3806692635c4f138cd36dea6fb Mon Sep 17 00:00:00 2001 From: James Morris Date: Wed, 6 Apr 2022 18:11:46 +0100 Subject: [PATCH] The big cleanup (#2328) * Trying out a tooltip alternative for combined graph in new dashboard refs: https://github.com/TryGhost/Team/issues/1467 * Trying out a different type of interaction with the combined graph for the new dashboard that includes different style refs: https://github.com/TryGhost/Team/issues/1467 * Working through the interface and code to majorly clean up for the new Dashboard refs: https://github.com/TryGhost/Team/issues/1462 - lots of moving around css - trying out some different layouts - refactoring lots of code - known bug: paid graphs don't work - known bug: without newsletters, layout breaks * Finishing up the basic styling of the new dashboard to be more presentable refs: https://github.com/TryGhost/Team/issues/1462 - add an animation between the top metrics on combined graph - ensure all graphs are responsive to parent container - refactor many of the components and tidy up the styles - tighten up spacing, headers, chart heights and more - make the tooltip hovers a little more presentable - balance the colors to be more muted, for the moment - a million other tiny tweaks --- app/components/dashboard/dashboard-v5.hbs | 17 +- app/components/dashboard/v5/chart-anchor.hbs | 68 +- app/components/dashboard/v5/chart-anchor.js | 81 +- app/components/dashboard/v5/chart-email.hbs | 57 +- app/components/dashboard/v5/chart-email.js | 35 +- .../dashboard/v5/chart-engagement.hbs | 20 +- .../dashboard/v5/chart-engagement.js | 6 +- .../v5/chart-members-counts-insert.hbs | 23 - .../v5/chart-members-counts-insert.js | 64 -- .../dashboard/v5/chart-members-counts.hbs | 26 - .../dashboard/v5/chart-members-counts.js | 64 -- .../dashboard/v5/chart-monthly-revenue.hbs | 14 - .../dashboard/v5/chart-monthly-revenue.js | 107 --- .../dashboard/v5/chart-paid-members.hbs | 35 +- .../dashboard/v5/chart-paid-members.js | 56 +- .../dashboard/v5/chart-paid-mix.hbs | 29 +- app/components/dashboard/v5/chart-paid-mix.js | 35 +- .../dashboard/v5/chart-total-members.hbs | 10 - .../dashboard/v5/chart-total-members.js | 107 --- .../dashboard/v5/chart-total-paid.hbs | 14 - .../dashboard/v5/chart-total-paid.js | 107 --- .../dashboard/v5/parts/chart-metric.hbs | 20 + .../dashboard/v5/recent-activity.hbs | 103 +- app/components/dashboard/v5/recent-posts.hbs | 46 +- app/styles/layouts/dashboard.css | 879 +++++++++++------- 25 files changed, 923 insertions(+), 1100 deletions(-) delete mode 100644 app/components/dashboard/v5/chart-members-counts-insert.hbs delete mode 100644 app/components/dashboard/v5/chart-members-counts-insert.js delete mode 100644 app/components/dashboard/v5/chart-members-counts.hbs delete mode 100644 app/components/dashboard/v5/chart-members-counts.js delete mode 100644 app/components/dashboard/v5/chart-monthly-revenue.hbs delete mode 100644 app/components/dashboard/v5/chart-monthly-revenue.js delete mode 100644 app/components/dashboard/v5/chart-total-members.hbs delete mode 100644 app/components/dashboard/v5/chart-total-members.js delete mode 100644 app/components/dashboard/v5/chart-total-paid.hbs delete mode 100644 app/components/dashboard/v5/chart-total-paid.js create mode 100644 app/components/dashboard/v5/parts/chart-metric.hbs diff --git a/app/components/dashboard/dashboard-v5.hbs b/app/components/dashboard/dashboard-v5.hbs index 18335cd5b..15ddf034e 100644 --- a/app/components/dashboard/dashboard-v5.hbs +++ b/app/components/dashboard/dashboard-v5.hbs @@ -7,7 +7,7 @@
-
+
Unknown option{{/if}}
+
+ March 31, 2022 +
{{#if this.hasPaidTiers}} @@ -42,15 +45,15 @@ {{#if this.areNewslettersEnabled}}
+
+ +
+
- -
- -
{{else}}
@@ -76,7 +79,9 @@
- +
+ +
diff --git a/app/components/dashboard/v5/chart-anchor.hbs b/app/components/dashboard/v5/chart-anchor.hbs index 4e5b852f3..687544bed 100644 --- a/app/components/dashboard/v5/chart-anchor.hbs +++ b/app/components/dashboard/v5/chart-anchor.hbs @@ -1,41 +1,51 @@
- {{#if this.hasPaidTiers}} - - {{/if}}
+
{{#if this.loading}} -
+
{{else}} - +
+ 1,000 + 750 + 500 + 250 +
+
+ +
{{/if}}
diff --git a/app/components/dashboard/v5/chart-anchor.js b/app/components/dashboard/v5/chart-anchor.js index 4199a9078..9454332c9 100644 --- a/app/components/dashboard/v5/chart-anchor.js +++ b/app/components/dashboard/v5/chart-anchor.js @@ -1,8 +1,11 @@ import Component from '@glimmer/component'; +import moment from 'moment'; import {action} from '@ember/object'; import {inject as service} from '@ember/service'; import {tracked} from '@glimmer/tracking'; +const DATE_FORMAT = 'D MMM YYYY'; + export default class ChartAnchor extends Component { @service dashboardStats; @tracked chartDisplay = 'total'; @@ -16,6 +19,8 @@ export default class ChartAnchor extends Component { @action changeChartDisplay(type) { this.chartDisplay = type; + document.querySelector('#gh-dashboard5-bar').classList.remove('is-show'); + document.querySelector('#gh-dashboard5-anchor-tooltip').classList.remove('is-show'); } get chartShowingTotal() { @@ -112,17 +117,20 @@ export default class ChartAnchor extends Component { labels: labels, datasets: [{ data: data, - tension: 0.1, + tension: 0, cubicInterpolationMode: 'monotone', fill: true, - fillColor: '#F5FBFF', - backgroundColor: '#F5FBFF', + fillColor: '#F3F7FF', + backgroundColor: '#F3F7FF', pointRadius: 0, pointHitRadius: 10, - borderColor: '#14b8ff', - borderJoinStyle: 'miter', - maxBarThickness: 20, - minBarLength: 2 + pointBorderColor: '#5597F9', + pointBackgroundColor: '#5597F9', + pointHoverBackgroundColor: '#5597F9', + pointHoverBorderColor: '#5597F9', + pointHoverRadius: 0, + borderColor: '#5597F9', + borderJoinStyle: 'miter' }] }; } @@ -139,7 +147,46 @@ export default class ChartAnchor extends Component { }, layout: { padding: { - top: 20 + top: 0 + } + }, + hover: { + onHover: function (e) { + e.target.style.cursor = 'pointer'; + } + }, + tooltips: { + intersect: false, + mode: 'index', + displayColors: false, + backgroundColor: '#15171A', + xPadding: 7, + yPadding: 7, + cornerRadius: 5, + caretSize: 7, + caretPadding: 5, + bodyFontSize: 12.5, + titleFontSize: 12, + titleFontStyle: 'normal', + titleFontColor: 'rgba(255, 255, 255, 0.7)', + titleMarginBottom: 3, + callbacks: { + label: (tooltipItems, data) => { + let valueText = data.datasets[tooltipItems.datasetIndex].data[tooltipItems.index].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); + + if (this.chartDisplay === 'total') { + return `Total members: ${valueText}`; + } + if (this.chartDisplay === 'paid') { + return `Paid members: ${valueText}`; + } + if (this.chartDisplay === 'monthly') { + return `Monthly revenue (MRR): ${valueText}`; + } + }, + title: (tooltipItems) => { + return moment(tooltipItems[0].xLabel).format(DATE_FORMAT); + } } }, scales: { @@ -154,8 +201,10 @@ export default class ChartAnchor extends Component { maxTicksLimit: 5, fontColor: '#7C8B9A', padding: 8, - precision: 0 - } + precision: 0, + beginAtZero: false + }, + display: true }], xAxes: [{ gridLines: { @@ -165,10 +214,9 @@ export default class ChartAnchor extends Component { }, ticks: { display: false, - maxTicksLimit: 5, - autoSkip: true, maxRotation: 0, - minRotation: 0 + minRotation: 0, + beginAtZero: false }, type: 'time', time: { @@ -183,12 +231,17 @@ export default class ChartAnchor extends Component { quarter: 'MMM DD', year: 'MMM DD' } - } + }, + display: true }] } }; } + get chartHeight() { + return 160; + } + calculatePercentage(from, to) { if (from === 0) { if (to > 0) { diff --git a/app/components/dashboard/v5/chart-email.hbs b/app/components/dashboard/v5/chart-email.hbs index 451615b79..ab73be27e 100644 --- a/app/components/dashboard/v5/chart-email.hbs +++ b/app/components/dashboard/v5/chart-email.hbs @@ -1,30 +1,41 @@ -

Email

-
-
-
- {{format-number this.dataSubscribers.total}}/{{format-number this.dataSubscribers.paid}} paid -
- Newsletter subscribers +
+
+
-
-
{{format-number this.dataEmailsSent}}
- Sent in the past 30 days +
+
-
- {{#if this.loading}} -
- {{else}} - - {{/if}} -
-
{{this.currentOpenRate}}%
- Open rate +
+ + +
+ {{#if this.loading}} +
+ {{else}} +
+ 100% + 75% + 50% + 25% +
+
+ +
+ {{/if}}
diff --git a/app/components/dashboard/v5/chart-email.js b/app/components/dashboard/v5/chart-email.js index ee9a36225..969abbd5e 100644 --- a/app/components/dashboard/v5/chart-email.js +++ b/app/components/dashboard/v5/chart-email.js @@ -55,27 +55,44 @@ export default class ChartEmailOpenRate extends Component { datasets: [{ data, fill: false, - backgroundColor: '#14b8ff', - tension: 0.1, + backgroundColor: '#7BA4F3', cubicInterpolationMode: 'monotone', - pointRadius: 0, - pointHitRadius: 10, - borderColor: '#14b8ff', - borderJoinStyle: 'miter', - maxBarThickness: 15, - minBarLength: 2 + barThickness: 10 }] }; } get chartOptions() { return { + responsive: true, + maintainAspectRatio: false, title: { display: false }, legend: { display: false }, + hover: { + onHover: function (e) { + e.target.style.cursor = 'pointer'; + } + }, + tooltips: { + intersect: false, + mode: 'index', + displayColors: false, + backgroundColor: '#15171A', + xPadding: 7, + yPadding: 7, + cornerRadius: 5, + caretSize: 7, + caretPadding: 5, + bodyFontSize: 12.5, + titleFontSize: 12, + titleFontStyle: 'normal', + titleFontColor: 'rgba(255, 255, 255, 0.7)', + titleMarginBottom: 3 + }, scales: { yAxes: [{ gridLines: { @@ -110,6 +127,6 @@ export default class ChartEmailOpenRate extends Component { } get chartHeight() { - return 175; + return 150; } } diff --git a/app/components/dashboard/v5/chart-engagement.hbs b/app/components/dashboard/v5/chart-engagement.hbs index 55bc91a2b..973f066ce 100644 --- a/app/components/dashboard/v5/chart-engagement.hbs +++ b/app/components/dashboard/v5/chart-engagement.hbs @@ -1,15 +1,17 @@ -

Engagement

- -
+
-
-
{{this.data30Days}}/99 members
- Engaged over 30 days +
+
-
-
{{this.data7Days}}/99 members
- Engaged over 7 days +
+
diff --git a/app/components/dashboard/v5/chart-engagement.js b/app/components/dashboard/v5/chart-engagement.js index 012655794..e0c2f8b51 100644 --- a/app/components/dashboard/v5/chart-engagement.js +++ b/app/components/dashboard/v5/chart-engagement.js @@ -4,13 +4,13 @@ import {inject as service} from '@ember/service'; import {tracked} from '@glimmer/tracking'; const STATUS_OPTIONS = [{ - name: 'All members', + name: 'All', value: 'total' }, { - name: 'Paid members', + name: 'Paid', value: 'paid' }, { - name: 'Free members', + name: 'Free', value: 'free' }]; diff --git a/app/components/dashboard/v5/chart-members-counts-insert.hbs b/app/components/dashboard/v5/chart-members-counts-insert.hbs deleted file mode 100644 index 3aa7d017d..000000000 --- a/app/components/dashboard/v5/chart-members-counts-insert.hbs +++ /dev/null @@ -1,23 +0,0 @@ -
-
-

{{gh-pluralize this.totalMembers "Total member" without-count=true}}

-
{{format-number this.totalMembers}}
- {{#if this.hasTrends}} - - {{/if}} -
-
-

{{gh-pluralize this.paidMembers "Paid member" without-count=true}}

-
{{format-number this.paidMembers}}
- {{#if this.hasTrends}} - - {{/if}} -
-
-

{{gh-pluralize this.freeMembers "Free member" without-count=true}}

-
{{format-number this.freeMembers}}
- {{#if this.hasTrends}} - - {{/if}} -
-
\ No newline at end of file diff --git a/app/components/dashboard/v5/chart-members-counts-insert.js b/app/components/dashboard/v5/chart-members-counts-insert.js deleted file mode 100644 index 94dd52ab8..000000000 --- a/app/components/dashboard/v5/chart-members-counts-insert.js +++ /dev/null @@ -1,64 +0,0 @@ -import Component from '@glimmer/component'; -import {action} from '@ember/object'; -import {inject as service} from '@ember/service'; - -export default class ChartMembersCountInsert extends Component { - @service dashboardStats; - - @action - loadCharts() { - this.dashboardStats.loadMemberCountStats(); - } - - get totalMembers() { - return this.dashboardStats.memberCounts?.total ?? 0; - } - - get paidMembers() { - return this.dashboardStats.memberCounts?.paid ?? 0; - } - - get freeMembers() { - return this.dashboardStats.memberCounts?.free ?? 0; - } - - get hasTrends() { - return this.dashboardStats.memberCounts !== null && this.dashboardStats.memberCountsTrend !== null; - } - - get totalMembersTrend() { - return this.calculatePercentage(this.dashboardStats.memberCountsTrend.total, this.dashboardStats.memberCounts.total); - } - - get paidMembersTrend() { - return this.calculatePercentage(this.dashboardStats.memberCountsTrend.paid, this.dashboardStats.memberCounts.paid); - } - - get freeMembersTrend() { - return this.calculatePercentage(this.dashboardStats.memberCountsTrend.free, this.dashboardStats.memberCounts.free); - } - - calculatePercentage(from, to) { - if (from === 0) { - if (to > 0) { - return 100; - } - return 0; - } - - const percentage = (to - from) / from * 100; - if (Math.abs(percentage) < 0.05) { - // Round on two decimals - return Math.round(percentage * 100) / 100; - } - if (Math.abs(percentage) < 0.25) { - // Round on one decimal - return Math.round(percentage * 10) / 10; - } - if (Math.abs(percentage) < 1) { - // Round on 0.5 - return Math.round(percentage * 2) / 2; - } - return Math.round(percentage); - } -} diff --git a/app/components/dashboard/v5/chart-members-counts.hbs b/app/components/dashboard/v5/chart-members-counts.hbs deleted file mode 100644 index 4cddd87ac..000000000 --- a/app/components/dashboard/v5/chart-members-counts.hbs +++ /dev/null @@ -1,26 +0,0 @@ -
-

Overview

-
-
-
-

{{gh-pluralize this.totalMembers "Total member" without-count=true}}

-
{{format-number this.totalMembers}}
- {{#if this.hasTrends}} - - {{/if}} -
-
-

{{gh-pluralize this.paidMembers "Paid member" without-count=true}}

-
{{format-number this.paidMembers}}
- {{#if this.hasTrends}} - - {{/if}} -
-
-

{{gh-pluralize this.freeMembers "Free member" without-count=true}}

-
{{format-number this.freeMembers}}
- {{#if this.hasTrends}} - - {{/if}} -
-
\ No newline at end of file diff --git a/app/components/dashboard/v5/chart-members-counts.js b/app/components/dashboard/v5/chart-members-counts.js deleted file mode 100644 index ff0db1daf..000000000 --- a/app/components/dashboard/v5/chart-members-counts.js +++ /dev/null @@ -1,64 +0,0 @@ -import Component from '@glimmer/component'; -import {action} from '@ember/object'; -import {inject as service} from '@ember/service'; - -export default class ChartMembersCounts extends Component { - @service dashboardStats; - - @action - loadCharts() { - this.dashboardStats.loadMemberCountStats(); - } - - get totalMembers() { - return this.dashboardStats.memberCounts?.total ?? 0; - } - - get paidMembers() { - return this.dashboardStats.memberCounts?.paid ?? 0; - } - - get freeMembers() { - return this.dashboardStats.memberCounts?.free ?? 0; - } - - get hasTrends() { - return this.dashboardStats.memberCounts !== null && this.dashboardStats.memberCountsTrend !== null; - } - - get totalMembersTrend() { - return this.calculatePercentage(this.dashboardStats.memberCountsTrend.total, this.dashboardStats.memberCounts.total); - } - - get paidMembersTrend() { - return this.calculatePercentage(this.dashboardStats.memberCountsTrend.paid, this.dashboardStats.memberCounts.paid); - } - - get freeMembersTrend() { - return this.calculatePercentage(this.dashboardStats.memberCountsTrend.free, this.dashboardStats.memberCounts.free); - } - - calculatePercentage(from, to) { - if (from === 0) { - if (to > 0) { - return 100; - } - return 0; - } - - const percentage = (to - from) / from * 100; - if (Math.abs(percentage) < 0.05) { - // Round on two decimals - return Math.round(percentage * 100) / 100; - } - if (Math.abs(percentage) < 0.25) { - // Round on one decimal - return Math.round(percentage * 10) / 10; - } - if (Math.abs(percentage) < 1) { - // Round on 0.5 - return Math.round(percentage * 2) / 2; - } - return Math.round(percentage); - } -} diff --git a/app/components/dashboard/v5/chart-monthly-revenue.hbs b/app/components/dashboard/v5/chart-monthly-revenue.hbs deleted file mode 100644 index ab9653860..000000000 --- a/app/components/dashboard/v5/chart-monthly-revenue.hbs +++ /dev/null @@ -1,14 +0,0 @@ -
$32
-Monthly revenue (MMR) - -{{#if this.loading}} -
-{{else}} - -{{/if}} \ No newline at end of file diff --git a/app/components/dashboard/v5/chart-monthly-revenue.js b/app/components/dashboard/v5/chart-monthly-revenue.js deleted file mode 100644 index 89f4d06ad..000000000 --- a/app/components/dashboard/v5/chart-monthly-revenue.js +++ /dev/null @@ -1,107 +0,0 @@ -import Component from '@glimmer/component'; -import {action} from '@ember/object'; -import {inject as service} from '@ember/service'; - -export default class ChartMonthlyRevenue extends Component { - @service dashboardStats; - - /** - * Call this method when you need to fetch new data from the server. - */ - @action - loadCharts() { - // The dashboard stats service will take care or reusing and limiting API-requests between charts - this.dashboardStats.loadMrrStats(); - } - - get loading() { - return this.dashboardStats.mrrStats === null; - } - - get chartType() { - return 'line'; - } - - get chartData() { - const stats = this.dashboardStats.filledMrrStats; - const labels = stats.map(stat => stat.date); - const data = stats.map(stat => stat.mrr); - - return { - labels: labels, - datasets: [{ - data: data, - tension: 0.1, - cubicInterpolationMode: 'monotone', - fill: true, - fillColor: '#F5FBFF', - backgroundColor: '#F5FBFF', - pointRadius: 0, - pointHitRadius: 10, - borderColor: '#14b8ff', - borderJoinStyle: 'miter', - maxBarThickness: 20, - minBarLength: 2 - }] - }; - } - - get chartOptions() { - return { - title: { - display: false - }, - legend: { - display: false - }, - scales: { - yAxes: [{ - gridLines: { - drawTicks: false, - display: false, - drawBorder: false - }, - ticks: { - display: false, - maxTicksLimit: 5, - fontColor: '#7C8B9A', - padding: 8, - precision: 0 - } - }], - xAxes: [{ - gridLines: { - drawTicks: false, - display: false, - drawBorder: false - }, - ticks: { - display: false, - maxTicksLimit: 5, - autoSkip: true, - maxRotation: 0, - minRotation: 0 - }, - type: 'time', - time: { - displayFormats: { - millisecond: 'MMM DD', - second: 'MMM DD', - minute: 'MMM DD', - hour: 'MMM DD', - day: 'MMM DD', - week: 'MMM DD', - month: 'MMM DD', - quarter: 'MMM DD', - year: 'MMM DD' - } - } - }] - } - }; - } - - get chartHeight() { - return 200; - } -} diff --git a/app/components/dashboard/v5/chart-paid-members.hbs b/app/components/dashboard/v5/chart-paid-members.hbs index c7d8c1a98..be3e14808 100644 --- a/app/components/dashboard/v5/chart-paid-members.hbs +++ b/app/components/dashboard/v5/chart-paid-members.hbs @@ -1,16 +1,23 @@ -Paid members + @label="Paid member" /> -{{#if this.loading}} -
-{{else}} -
- -
-{{/if}} +
+ {{#if this.loading}} +
+ {{else}} +
+ 10 + 5 + 0 + -5 +
+
+ +
+ {{/if}} +
diff --git a/app/components/dashboard/v5/chart-paid-members.js b/app/components/dashboard/v5/chart-paid-members.js index fcde54be0..530158a17 100644 --- a/app/components/dashboard/v5/chart-paid-members.js +++ b/app/components/dashboard/v5/chart-paid-members.js @@ -1,7 +1,10 @@ import Component from '@glimmer/component'; +import moment from 'moment'; import {action} from '@ember/object'; import {inject as service} from '@ember/service'; +const DATE_FORMAT = 'D MMM YYYY'; + export default class ChartPaidMembers extends Component { @service dashboardStats; @@ -34,38 +37,53 @@ export default class ChartPaidMembers extends Component { { data: newData, fill: true, - borderColor: '#9B90F9', - backgroundColor: '#9B90F9', - tension: 0.1, - barThickness: 10, - minBarLength: 3, - borderWidth: 2, - borderRadius: 5 + backgroundColor: '#7BA4F3', + barThickness: 10 },{ data: canceledData, fill: true, - borderColor: '#E28B9D', - backgroundColor: '#E28B9D', - tension: 0.1, - barThickness: 10, - minBarLength: 3, - borderWidth: 2, - borderRadius: 5 + backgroundColor: '#E5E5E5', + barThickness: 10 }] }; } get chartOptions() { return { - animation: { - duration: 0 - }, + responsive: true, + maintainAspectRatio: false, title: { display: false }, legend: { display: false }, + hover: { + onHover: function (e) { + e.target.style.cursor = 'pointer'; + } + }, + tooltips: { + intersect: false, + mode: 'index', + displayColors: false, + backgroundColor: '#15171A', + xPadding: 7, + yPadding: 7, + cornerRadius: 5, + caretSize: 7, + caretPadding: 5, + bodyFontSize: 12.5, + titleFontSize: 12, + titleFontStyle: 'normal', + titleFontColor: 'rgba(255, 255, 255, 0.7)', + titleMarginBottom: 3, + callbacks: { + title: (tooltipItems) => { + return moment(tooltipItems[0].xLabel).format(DATE_FORMAT); + } + } + }, scales: { yAxes: [{ offset: true, @@ -113,4 +131,8 @@ export default class ChartPaidMembers extends Component { } }; } + + get chartHeight() { + return 125; + } } diff --git a/app/components/dashboard/v5/chart-paid-mix.hbs b/app/components/dashboard/v5/chart-paid-mix.hbs index 90c600ba7..97d1548f7 100644 --- a/app/components/dashboard/v5/chart-paid-mix.hbs +++ b/app/components/dashboard/v5/chart-paid-mix.hbs @@ -17,19 +17,20 @@
{{/if}} -Paid mix + @label="Paid mix" /> -{{#if this.loading}} -
-{{else}} -
- -
-{{/if}} +
+ {{#if this.loading}} +
+ {{else}} +
+ +
+ {{/if}} +
diff --git a/app/components/dashboard/v5/chart-paid-mix.js b/app/components/dashboard/v5/chart-paid-mix.js index 6f4925401..b7cd5a02b 100644 --- a/app/components/dashboard/v5/chart-paid-mix.js +++ b/app/components/dashboard/v5/chart-paid-mix.js @@ -66,8 +66,7 @@ export default class ChartPaidMix extends Component { datasets: [{ data: [this.dashboardStats.paidMembersByCadence.monthly, this.dashboardStats.paidMembersByCadence.annual], fill: false, - backgroundColor: ['#14b8ff'], - tension: 0.1 + backgroundColor: ['#7BA4F3'] }] }; } @@ -80,22 +79,48 @@ export default class ChartPaidMix extends Component { datasets: [{ data, fill: false, - backgroundColor: ['#14b8ff'], - borderWidth: 3, - tension: 0.1 + backgroundColor: ['#7BA4F3'], + borderWidth: 3 }] }; } get chartOptions() { return { + responsive: true, + maintainAspectRatio: false, cutoutPercentage: (this.mode === 'cadence' ? 85 : 75), legend: { display: false }, animation: { duration: 0 + }, + hover: { + onHover: function (e) { + e.target.style.cursor = 'pointer'; + } + }, + tooltips: { + intersect: false, + mode: 'index', + displayColors: false, + backgroundColor: '#15171A', + xPadding: 7, + yPadding: 7, + cornerRadius: 5, + caretSize: 7, + caretPadding: 5, + bodyFontSize: 12.5, + titleFontSize: 12, + titleFontStyle: 'normal', + titleFontColor: 'rgba(255, 255, 255, 0.7)', + titleMarginBottom: 3 } }; } + + get chartHeight() { + return 75; + } } diff --git a/app/components/dashboard/v5/chart-total-members.hbs b/app/components/dashboard/v5/chart-total-members.hbs deleted file mode 100644 index 935acd912..000000000 --- a/app/components/dashboard/v5/chart-total-members.hbs +++ /dev/null @@ -1,10 +0,0 @@ -{{#if this.loading}} -
-{{else}} - - -{{/if}} \ No newline at end of file diff --git a/app/components/dashboard/v5/chart-total-members.js b/app/components/dashboard/v5/chart-total-members.js deleted file mode 100644 index 78f7302b8..000000000 --- a/app/components/dashboard/v5/chart-total-members.js +++ /dev/null @@ -1,107 +0,0 @@ -import Component from '@glimmer/component'; -import {action} from '@ember/object'; -import {inject as service} from '@ember/service'; - -export default class ChartTotalMembers extends Component { - @service dashboardStats; - - /** - * Call this method when you need to fetch new data from the server. - */ - @action - loadCharts() { - // The dashboard stats service will take care or reusing and limiting API-requests between charts - this.dashboardStats.loadMemberCountStats(); - } - - get loading() { - return this.dashboardStats.memberCountStats === null; - } - - get chartType() { - return 'line'; - } - - get chartData() { - const stats = this.dashboardStats.filledMemberCountStats; - const labels = stats.map(stat => stat.date); - const data = stats.map(stat => stat.paid + stat.free + stat.comped); - - return { - labels: labels, - datasets: [{ - data: data, - tension: 0.1, - cubicInterpolationMode: 'monotone', - fill: true, - fillColor: '#F5FBFF', - backgroundColor: '#F5FBFF', - pointRadius: 0, - pointHitRadius: 10, - borderColor: '#14b8ff', - // borderJoinStyle: 'miter', - maxBarThickness: 20, - minBarLength: 2 - }] - }; - } - - get chartOptions() { - return { - title: { - display: false - }, - legend: { - display: false - }, - scales: { - yAxes: [{ - gridLines: { - drawTicks: false, - display: false, - drawBorder: false - }, - ticks: { - display: false, - maxTicksLimit: 5, - fontColor: '#7C8B9A', - padding: 8, - precision: 0 - } - }], - xAxes: [{ - gridLines: { - drawTicks: false, - display: false, - drawBorder: false - }, - ticks: { - display: false, - maxTicksLimit: 5, - autoSkip: true, - maxRotation: 0, - minRotation: 0 - }, - type: 'time', - time: { - displayFormats: { - millisecond: 'MMM DD', - second: 'MMM DD', - minute: 'MMM DD', - hour: 'MMM DD', - day: 'MMM DD', - week: 'MMM DD', - month: 'MMM DD', - quarter: 'MMM DD', - year: 'MMM DD' - } - } - }] - } - }; - } - - get chartHeight() { - return 50; - } -} diff --git a/app/components/dashboard/v5/chart-total-paid.hbs b/app/components/dashboard/v5/chart-total-paid.hbs deleted file mode 100644 index 56aded422..000000000 --- a/app/components/dashboard/v5/chart-total-paid.hbs +++ /dev/null @@ -1,14 +0,0 @@ -
$999
-Total paid - -{{#if this.loading}} -
-{{else}} - -{{/if}} \ No newline at end of file diff --git a/app/components/dashboard/v5/chart-total-paid.js b/app/components/dashboard/v5/chart-total-paid.js deleted file mode 100644 index 3c63c2b77..000000000 --- a/app/components/dashboard/v5/chart-total-paid.js +++ /dev/null @@ -1,107 +0,0 @@ -import Component from '@glimmer/component'; -import {action} from '@ember/object'; -import {inject as service} from '@ember/service'; - -export default class ChartTotalPaid extends Component { - @service dashboardStats; - - /** - * Call this method when you need to fetch new data from the server. - */ - @action - loadCharts() { - // The dashboard stats service will take care or reusing and limiting API-requests between charts - this.dashboardStats.loadMemberCountStats(); - } - - get loading() { - return this.dashboardStats.memberCountStats === null; - } - - get chartType() { - return 'line'; - } - - get chartData() { - const stats = this.dashboardStats.filledMemberCountStats; - const labels = stats.map(stat => stat.date); - const data = stats.map(stat => stat.paid); - - return { - labels, - datasets: [{ - data, - tension: 0.1, - cubicInterpolationMode: 'monotone', - fill: true, - fillColor: '#F5FBFF', - backgroundColor: '#F5FBFF', - pointRadius: 0, - pointHitRadius: 10, - borderColor: '#14b8ff', - borderJoinStyle: 'miter', - maxBarThickness: 20, - minBarLength: 2 - }] - }; - } - - get chartOptions() { - return { - title: { - display: false - }, - legend: { - display: false - }, - scales: { - yAxes: [{ - gridLines: { - drawTicks: false, - display: false, - drawBorder: false - }, - ticks: { - display: false, - maxTicksLimit: 5, - fontColor: '#7C8B9A', - padding: 8, - precision: 0 - } - }], - xAxes: [{ - gridLines: { - drawTicks: false, - display: false, - drawBorder: false - }, - ticks: { - display: false, - maxTicksLimit: 5, - autoSkip: true, - maxRotation: 0, - minRotation: 0 - }, - type: 'time', - time: { - displayFormats: { - millisecond: 'MMM DD', - second: 'MMM DD', - minute: 'MMM DD', - hour: 'MMM DD', - day: 'MMM DD', - week: 'MMM DD', - month: 'MMM DD', - quarter: 'MMM DD', - year: 'MMM DD' - } - } - }] - } - }; - } - - get chartHeight() { - return 50; - } -} diff --git a/app/components/dashboard/v5/parts/chart-metric.hbs b/app/components/dashboard/v5/parts/chart-metric.hbs new file mode 100644 index 000000000..c3624d793 --- /dev/null +++ b/app/components/dashboard/v5/parts/chart-metric.hbs @@ -0,0 +1,20 @@ +
+
+
+ {{@label}} +
+ {{#if @value}} +
+ {{@value}} + {{#if @trends}} + + {{/if}} +
+ {{/if}} +
+ {{#if @extra}} +
+ {{@extra}} +
+ {{/if}} +
\ No newline at end of file diff --git a/app/components/dashboard/v5/recent-activity.hbs b/app/components/dashboard/v5/recent-activity.hbs index 08f2fcd57..81ae6e659 100644 --- a/app/components/dashboard/v5/recent-activity.hbs +++ b/app/components/dashboard/v5/recent-activity.hbs @@ -1,59 +1,46 @@ -{{#if this.shouldDisplay}} -
-

Recent activity

-
- {{#let (members-event-fetcher filter=(members-event-filter excludeEmailEvents=true) pageSize=5) as |eventsFetcher|}} - {{#if eventsFetcher.isError}} -

- There was an error loading events - {{#if eventsFetcher.errorMessage}} - {{eventsFetcher.errorMessage}} - {{/if}} -

- {{/if}} - - {{#if eventsFetcher.isLoading}} - Loading... - {{else}} -
- {{#if eventsFetcher.data}} -
    - {{#each eventsFetcher.data as |event|}} - {{#let (parse-member-event event) as |parsedEvent|}} -
  • - -
    - {{svg-jar parsedEvent.icon}} -
    -
    - {{parsedEvent.subject}} -
    -
    - {{parsedEvent.action}} - {{parsedEvent.object}} - {{parsedEvent.info}} -
    -
    -
    -
    - {{moment-from-now parsedEvent.timestamp}} -
  • - {{/let}} - {{/each}} -
- {{else}} -
- {{svg-jar "no-data-list"}} - No member activity available. -
- {{/if}} -
- - - {{/if}} - {{/let}} -
+
+
+
-{{/if}} +
+ {{#let (members-event-fetcher filter=(members-event-filter excludeEmailEvents=true) pageSize=5) as |eventsFetcher|}} + {{#if eventsFetcher.isError}} +
+

There was an error loading events

+ {{#if eventsFetcher.errorMessage}} + {{eventsFetcher.errorMessage}} + {{/if}} +
+ {{/if}} + {{#if eventsFetcher.isLoading}} +
+

Loading...

+
+ {{else}} + {{#if eventsFetcher.data}} + {{#each eventsFetcher.data as |event|}} + {{#let (parse-member-event event) as |parsedEvent|}} +
+ + {{parsedEvent.subject}} + {{parsedEvent.action}} + {{parsedEvent.object}} + {{parsedEvent.info}} + + {{!-- {{moment-from-now parsedEvent.timestamp}} --}} +
+ {{/let}} + {{/each}} + {{else}} +
+ {{svg-jar "no-data-list"}} +

No activity yet.

+
+ {{/if}} + {{/if}} + {{/let}} +
+ +
\ No newline at end of file diff --git a/app/components/dashboard/v5/recent-posts.hbs b/app/components/dashboard/v5/recent-posts.hbs index fcdc1dd02..bcc6cfc98 100644 --- a/app/components/dashboard/v5/recent-posts.hbs +++ b/app/components/dashboard/v5/recent-posts.hbs @@ -1,28 +1,22 @@ - -
    -
  1. -

    Recent posts

    - {{!-- -
    Title
    -
    Sends
    -
    Opens
    - --}} -
  2. - {{#each this.posts as |post|}} - - {{else}} -
  3. -
    -

    No published posts yet.

    +
    +
    + +
    +
    + {{#each this.posts as |post|}} +
    + + {{post.title}} +
    -
  4. - {{/each}} -
- - +
\ No newline at end of file diff --git a/app/styles/layouts/dashboard.css b/app/styles/layouts/dashboard.css index 4cd380117..cd12882ed 100644 --- a/app/styles/layouts/dashboard.css +++ b/app/styles/layouts/dashboard.css @@ -994,8 +994,8 @@ a.gh-dashboard-container { .prototype-paid-mix-dropdown { position: absolute; - right: 15px; - top: 25px; + right: 0px; + top: 8px; } .prototype-counts { @@ -1028,7 +1028,7 @@ a.gh-dashboard-container { } .prototype-box { - border: 1px solid #ebeef0; + border: 1px solid var(--whitegrey); padding: 28px; position: relative; } @@ -1037,71 +1037,17 @@ a.gh-dashboard-container { font-size: 24px; } -.gh-dashboard5 { - max-width: 1230px; - margin: 0 auto; -} - -.gh-dashboard5-split { - display: grid; - gap: 24px; - grid-template-columns: 1fr 1fr; -} - -.gh-dashboard5-split.is-third { - grid-template-columns: 2fr 1fr; -} - -.gh-dashboard5-section { - flex: 1; - display: flex; - flex-direction: column; - margin-bottom: 24px; -} - -.gh-dashboard5-container { - display: flex; - flex-direction: row; - align-items: stretch; - gap: 0; -} - -.gh-dashboard5-box { - flex: 1; - border: 1px solid var(--whitegrey); - padding: 28px; - border-radius: 3px; - display: flex; - flex-direction: column; - align-items: flex-start; - position: relative; /* Temporarily added for absolute positioned prototype-paid-mix-dropdown */ -} - -.gh-dashboard5-container .gh-dashboard5-box { - border-radius: 0; - border-width: 1px 0 1px 1px; -} - -.gh-dashboard5-container .gh-dashboard5-box:first-child { - border-radius: 3px 0 0 3px; -} - -.gh-dashboard5-container .gh-dashboard5-box:last-child { - border-radius: 0 3px 3px 0; - border-width: 1px; -} - .gh-dashboard5-number { display: flex; align-items: flex-end; - font-size: 3rem; + font-size: 2.4rem; line-height: 4rem; - font-weight: 600; + font-weight: 700; color: var(--black); letter-spacing: -.1px; line-height: 1; white-space: nowrap; - margin: 0 0 4px; + margin: 4px 0 2px; gap: 10px; } @@ -1123,47 +1069,12 @@ a.gh-dashboard-container { margin: 0 0.5em 0 0; } -.gh-dashboard5-metric { - display: flex; - align-items: center; +.gh-dashboard5-info { font-size: 1.1rem; text-transform: uppercase; - font-weight: 500; + font-weight: 700; letter-spacing: .2px; - margin: 0 0 8px; - padding: 0; - color: var(--midgrey); -} - -.gh-dashboard5-metric.is-main { - margin-bottom: 24px; -} - -.gh-dashboard5-metric.is-split { - display: flex; - flex-direction: column; - align-items: flex-start; - font-size: 1.4rem; - text-transform: none; - margin: 0 0 32px; -} - -.gh-dashboard5-metric.is-split small { - font-size: 1.1rem; - text-transform: none; - font-weight: 500; - letter-spacing: .2px; - margin: 4px 0 0 0; - padding: 0; - color: var(--midgrey); -} - -.gh-dashboard5-info { - font-size: 1.4rem; - text-transform: none; - font-weight: 500; - letter-spacing: .2px; - margin: 2px 0 0; + margin: 0 0 6px; padding: 0; color: var(--midgrey); } @@ -1172,27 +1083,14 @@ a.gh-dashboard-container { margin: 0 0 20px; } -.gh-dashboard5-percentage { - flex: 0; - background: var(--whitegrey-d1); - border-radius: 3px; - font-size: 1.2rem; - line-height: 1; - font-weight: 500; - letter-spacing: 0; - color: var(--midgrey); - padding: 2px 4px; - margin: 5px 0 3px 0; -} - -.gh-dashboard5-percentage.is-positive { - background: color-mod(var(--green) a(13%)); - color: color-mod(var(--green) l(-5%)); -} - -.gh-dashboard5-percentage.is-negative { - background: color-mod(var(--yellow) a(20%)); - color: color-mod(var(--yellow) l(-8%)); +.gh-dashboard5-date { + font-size: 1.1rem; + text-transform: none; + font-weight: 600; + letter-spacing: .2px; + margin: 2px 0 0; + padding: 0; + color: var(--midlightgrey); } .gh-dashboard5-header { @@ -1213,93 +1111,6 @@ a.gh-dashboard-container { margin: 0 -8px 6px 0; } -.gh-dashboard5-email > h3 { - display: none; -} - -.gh-dashboard5-email .gh-dashboard5-container { - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: 1fr 1fr; - gap: 0; -} -/* -.gh-dashboard5-email .gh-dashboard5-box { - border-width: 1px 0 1px 1px; - border-radius: 0; -} - -.gh-dashboard5-email .gh-dashboard5-box:nth-child(1) { - grid-column: 1; - grid-row: 1; - border-radius: 3px 0 0 0; -} - -.gh-dashboard5-email .gh-dashboard5-box:nth-child(2) { - grid-column: 1; - grid-row: 2; - border-width: 0 0 1px 1px; - border-radius: 0 0 0 3px; -} - -.gh-dashboard5-email .gh-dashboard5-box:nth-child(3) { - grid-column: 2; - grid-row: 1 / span 2; -} */ - -.gh-dashboard5-growth { - display: flex; - margin-top: -25px; -} - -/* .gh-dashboard5-growth .gh-dashboard5-box:nth-child(1) { - grid-column: 1 / span 2; - grid-row: 1; - border-radius: 3px 3px 0 0; - border-width: 1px; -} */ - -.gh-dashboard5-growth .gh-dashboard5-box:nth-child(1) { - /* grid-column: 1; - grid-row: 1; */ - flex: 2; - border-radius: 3px 0 0 0; - border-width: 1px; - padding-top: 32px; -} - -.gh-dashboard5-growth .gh-dashboard5-box:nth-child(2) { - /* grid-column: 2; - grid-row: 1; */ - flex: 1; - border-radius: 0 3px 0 0; - border-width: 1px 1px 1px 0; - padding-top: 32px; -} - -/* .gh-dashboard5-growth .gh-dashboard5-box:nth-child(3) { - grid-column: 3; - grid-row: 2; - border-radius: 0 0 0 3px; - border-width: 0 1px 1px 0; -} - -.gh-dashboard5-growth .gh-dashboard5-box:nth-child(4) { - grid-column: 4; - grid-row: 2; - border-radius: 0 0 3px 0; - border-width: 0 1px 1px 0; -} */ - -.gh-dashboard5-growth { - height: 250px; -} - -.gh-dashboard5-growth canvas { - width: 100%; - height: 100%; -} - .gh-dashboard5 .gh-dashboard-box { margin-bottom: 0; } @@ -1352,88 +1163,20 @@ a.gh-dashboard-container { right: 20px; } -.gh-dashboard5-engagement { - position: relative; -} - -.gh-dashboard5-engagement > h3 { - display: none; -} - -.gh-dashboard5-engagement > .gh-dashboard5-box { - padding: 0; - align-items: stretch; -} - -.gh-dashboard5-engagement .gh-dashboard5-insert { - display: flex; - flex-direction: row; - margin: 20px 28px; - flex: 1; -} - -.gh-dashboard5-engagement .gh-dashboard5-insert .gh-dashboard5-box { - border-radius: 0; - border-width: 0 1px 0 0; - padding: 8px 24px 0 0; -} - -.gh-dashboard5-engagement .gh-dashboard5-insert .gh-dashboard5-box:first-child { - justify-content: flex-end; - padding-top: 0; - padding-bottom: 24px; -} - -.gh-dashboard5-engagement .gh-dashboard5-insert .gh-dashboard5-box:last-child { - border-left-width: 0; - flex-direction: column; - justify-content: flex-end; - padding-top: 24px; - padding-bottom: 0; -} - -.gh-dashboard5-split .gh-dashboard5-engagement .gh-dashboard5-insert { - display: flex; - flex-direction: column; - margin: 20px 28px; - flex: 1; -} - -.gh-dashboard5-split .gh-dashboard5-engagement .gh-dashboard5-insert .gh-dashboard5-box { - border-radius: 0; - border-width: 0 0 1px; - padding: 8px 24px 24px 0; -} - -.gh-dashboard5-split .gh-dashboard5-engagement .gh-dashboard5-insert .gh-dashboard5-box:first-child { - padding-top: 0; - padding-bottom: 24px; -} - -.gh-dashboard5-split .gh-dashboard5-engagement .gh-dashboard5-insert .gh-dashboard5-box:last-child { - border-bottom-width: 0; - flex: 1; - display: flex; - flex-direction: column; - justify-content: flex-end; - padding-top: 24px; - padding-bottom: 0; -} - .gh-dashboard5-section .prototype-selection { position: absolute; - top: 18px; - right: 12px; + top: 10px; + right: 0; } .gh-dashboard5-section .ember-power-select-selected-item { - color: var(--darkgrey); + text-align: right; font-size: 1.1rem; text-transform: uppercase; - font-weight: 500; + font-weight: 600; letter-spacing: .2px; - margin: 0 0 8px; - padding: 0; + color: var(--midgrey); + white-space: nowrap; } .gh-dashboard5 .gh-list-header { @@ -1446,106 +1189,568 @@ a.gh-dashboard-container { color: rgb(21, 23, 26); } -.gh-dashboard5-posts .gh-dashboard5-box { - display: flex; - flex-direction: column; - justify-content: space-between; - padding-bottom: 16px; -} - .gh-dashboard5-posts .footer { padding-top: 12px; } -.gh-dashboard5-email h3 { +.gh-dashboard5-tooltip { + position: absolute; + bottom: 16px; + right: 16px; display: none; } -.gh-dashboard5-email canvas { - border-bottom: 1px solid #ebeef0; +.gh-dashboard5-tooltip.is-show { + display: block; } -.gh-dashboard5-email > .gh-dashboard5-box { +.gh-dashboard5-stats button { + position: relative; +} + +.gh-dashboard5-normal { + visibility: visible; + opacity: 1; +} + +.gh-dashboard5-normal.is-fade { + opacity: 0.2; +} + +.gh-dashboard5-normal.is-hide { + visibility: hidden; +} + +#gh-dashboard5-anchor-selection { + opacity: 1; +} + +#gh-dashboard5-anchor-selection.is-hide { + opacity: 0; +} + +#gh-dashboard5-anchor-globaldate { + display: none; + position: absolute; + top: 28px; + right: 31px; + color: #394047; + font-size: 1.1rem; + text-transform: uppercase; + font-weight: 500; + letter-spacing: .2px; + margin: 0 0 8px; + padding: 0; +} + +#gh-dashboard5-anchor-globaldate.is-show { + display: block; +} + +#gh-dashboard5-bar { + display: none; + position: absolute; + top: 0; + left: 100px; + width: 1px; + height: 100%; + background: var(--lightgrey); + transition: left 125ms linear; +} + +#gh-dashboard5-bar.is-show { + display: block; +} + +#gh-dashboard5 canvas { + z-index: 99; +} + +#gh-dashboard5-anchor-tooltip { + position: absolute; + bottom: 14px; + right: 16px; + opacity: 0; + transition: transform 125ms ease-out; + transform: translateY(8px); +} + +#gh-dashboard5-anchor-tooltip.is-show { + opacity: 1; + transform: translateY(0); +} + +#gh-dashboard5-anchor-tooltip-value { + text-align: right; + font-size: 2rem; + line-height: 4rem; + font-weight: 700; + color: var(--black); + letter-spacing: -.1px; + line-height: 1; + white-space: nowrap; +} + +#gh-dashboard5-anchor-tooltip-label { + text-align: right; + font-size: 1.2rem; + text-transform: none; + font-weight: 500; + letter-spacing: .2px; + margin: 2px 0 0; + padding: 0; + color: var(--midgrey); +} + + +/* --------------------------------- +Dashboard v5 Layout */ + +.gh-dashboard5 { + max-width: 1230px; + margin: 0 auto; +} + +.gh-dashboard5-split { + display: grid; + gap: 24px; + grid-template-columns: 1fr 1fr; +} + +.gh-dashboard5-split.is-third { + grid-template-columns: 1fr 3fr; +} + +.gh-dashboard5-section { + flex: 1; + display: flex; + flex-direction: column; + margin-bottom: 24px; +} + +.gh-dashboard5-container { + display: flex; + flex-direction: row; + align-items: stretch; + gap: 0; +} + +.gh-dashboard5-box { + flex: 1; + border: 1px solid var(--whitegrey); + padding: 16px; + border-radius: 3px; + display: flex; + flex-direction: column; + position: relative; /* Temporarily added for absolute positioned prototype-paid-mix-dropdown */ + align-items: stretch; +} + +.gh-dashboard5-container .gh-dashboard5-box { + border-radius: 0; + border-width: 1px 0 1px 1px; +} + +.gh-dashboard5-container .gh-dashboard5-box:first-child { + border-radius: 3px 0 0 3px; +} + +.gh-dashboard5-container .gh-dashboard5-box:last-child { + border-radius: 0 3px 3px 0; + border-width: 1px; +} + + +/* --------------------------------- +Dashboard v5 Chart */ + +.gh-dashboard5-chart { + display: flex; + flex-direction: row; + flex: 1; +} + +.gh-dashboard5-chart-loading { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.gh-dashboard5-chart-ticks { + flex: none; + padding: 8px 24px 16px 16px; + font-size: 1.2rem; + text-transform: none; + font-weight: 500; + letter-spacing: .2px; + display: flex; + flex-direction: column; + justify-content: space-between; + font-size: 1.1rem; + letter-spacing: .2px; + color: var(--midgrey); + line-height: 1em; +} + +.gh-dashboard5-chart-container { + flex: 1; + position: relative; + width: 0%; /* hack for ChartJS responsive resizing */ + height: 100%; +} + + +/* --------------------------------- +Dashboard v5 Percentage */ + +.gh-dashboard5-percentage { + flex: 0; + background: var(--whitegrey-d1); + border-radius: 3px; + font-size: 1.2rem; + line-height: 1; + font-weight: 500; + letter-spacing: 0; + color: var(--midgrey); + padding: 2px 4px; + margin: 5px 0 3px 0; +} + +.gh-dashboard5-percentage.is-positive { + background: color-mod(var(--green) a(13%)); + color: color-mod(var(--green) l(-5%)); +} + +.gh-dashboard5-percentage.is-negative { + background: color-mod(var(--yellow) a(20%)); + color: color-mod(var(--yellow) l(-8%)); +} + + +/* --------------------------------- +Dashboard v5 Metric */ + +.gh-dashboard5-metric { + display: flex; + flex-direction: column; +} + +.gh-dashboard5-metric.is-stretch { + flex: 1; + justify-content: space-between; +} + +.gh-dashboard5-metric-data { + display: flex; + flex-direction: column; +} + +.gh-dashboard5-metric-label { + align-items: center; + font-size: 1.1rem; + text-transform: uppercase; + font-weight: 600; + letter-spacing: .2px; + line-height: 1em; + margin: 4px 0 8px; + padding: 0; + color: var(--middarkgrey); + white-space: nowrap; +} + +.gh-dashboard5-metric-value { + display: flex; + align-items: flex-end; + font-size: 2.2rem; + line-height: 4rem; + font-weight: 700; + color: var(--black); + letter-spacing: -.1px; + line-height: 1em; + white-space: nowrap; + margin: 4px 0 2px; + gap: 10px; +} + +.gh-dashboard5-metric-value.is-large { + font-size: 2.8rem; +} + +.gh-dashboard5-metric-extra { + font-size: 1.2rem; + text-transform: none; + font-weight: 500; + letter-spacing: .2px; + font-size: 1.1rem; + letter-spacing: .2px; + color: var(--midlightgrey); + line-height: 1em; +} + + +/* --------------------------------- +Dashboard v5 List */ + +.gh-dashboard5-list { + flex: 1; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.gh-dashboard5-list-header { + padding: 0 0 8px; +} + +.gh-dashboard5-list-body { + flex: 1; + display: flex; + flex-direction: column; + padding: 0 0 8px; +} + +.gh-dashboard5-list-item { + padding: 8px 0; +} + +.gh-dashboard5-list-item a { + font-weight: 500; + color: var(--black); +} + +.gh-dashboard5-list-footer { + border-top: 1px solid var(--whitegrey); + padding: 12px 0 0; +} + + +/* --------------------------------- +Dashboard v5 Section Anchor */ + +.gh-dashboard5-anchor { + position: relative; +} + +.gh-dashboard5-anchor .gh-dashboard5-box { padding: 0; align-items: stretch; } -.gh-dashboard5-email .gh-dashboard5-insert { - margin: 20px 28px; +.gh-dashboard5-anchor .gh-dashboard5-stats { + display: flex; + flex-direction: row; + width: 55%; + padding: 16px; + margin: 0 0 8px; +} + +.gh-dashboard5-anchor .gh-dashboard5-stats-button { + cursor: pointer; + position: relative; + flex: 1; + padding: 0 32px 0 0; + margin: 0; + text-align: left; +} + +.gh-dashboard5-anchor .gh-dashboard5-stats-highlight { + width: 1px; + height: 3px; + border-radius: 5px; + background: var(--whitegrey); + margin: 8px 0 0; + background: #5B98F2; + opacity: 0; + transition: width 175ms ease-out, opacity 125ms linear; +} + +.gh-dashboard5-anchor .gh-dashboard5-stats-button.is-selected .gh-dashboard5-stats-highlight { + width: 25px; + opacity: 1; +} + +.gh-dashboard5-anchor canvas { + border-left: 1px solid var(--whitegrey); +} + + +/* --------------------------------- +Dashboard v5 Section Growth */ + +.gh-dashboard5-growth { + display: flex; + margin-top: -25px; +} + +.gh-dashboard5-growth .gh-dashboard5-box:nth-child(1) { + flex: 2; + border-radius: 3px 0 0 0; + border-width: 1px; + padding-top: 16px; +} + +.gh-dashboard5-growth .gh-dashboard5-box:nth-child(2) { + flex: 1; + border-radius: 0 3px 0 0; + border-width: 1px 1px 1px 0; + padding-top: 16px; +} + +.gh-dashboard5-growth .gh-dashboard5-chart-ticks { + padding: 8px 24px 16px 0; + width: 55px; +} + +.gh-dashboard5-growth .gh-dashboard5-metric { + margin: 0 0 20px; +} + +.gh-dashboard5-growth canvas { + border-left: 1px solid var(--whitegrey); +} + + +/* --------------------------------- +Dashboard v5 Section Email */ + +.gh-dashboard5-email canvas { + border-bottom: 1px solid var(--whitegrey); +} + +.gh-dashboard5-email .gh-dashboard5-container { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 0; } -.gh-dashboard5-email .gh-dashboard5-insert .gh-dashboard5-box:nth-child(1) { +.gh-dashboard5-email .gh-dashboard5-insert { + display: grid; + grid-template-columns: 1fr 2fr; + grid-template-rows: 1fr 1fr; + gap: 0; +} + +.gh-dashboard5-email .gh-dashboard5-insert-item { + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +.gh-dashboard5-email .gh-dashboard5-insert-item:nth-child(1) { + /* --- Newsletter Subscribers */ grid-column: 1; grid-row: 1; - border-radius: 0; + padding: 4px 16px 16px 0; + border: 1px solid var(--whitegrey); border-width: 0 1px 1px 0; - padding: 8px 24px 24px 0; } -.gh-dashboard5-email .gh-dashboard5-insert .gh-dashboard5-box:nth-child(2) { +.gh-dashboard5-email .gh-dashboard5-insert-item:nth-child(2) { + /* --- Emails Sent */ grid-column: 1; grid-row: 2; - border-radius: 0; - border-width: 0 1px 0 0; - padding: 20px 24px 0 0; - display: flex; - flex-direction: column; - justify-content: flex-end; + padding: 16px 16px 0 0; + border-right: 1px solid var(--whitegrey); } -.gh-dashboard5-email .gh-dashboard5-insert .gh-dashboard5-box:nth-child(3) { +.gh-dashboard5-email .gh-dashboard5-insert-item:nth-child(3) { + /* --- Open Rate */ grid-column: 2; grid-row: 1 / span 2; - border-radius: 0; - border-width: 0; - padding: 8px 0 0 28px; - display: flex; - flex-direction: column; justify-content: space-between; + align-items: stretch; + padding: 4px 0 0 16px; } -.gh-dashboard5-anchor { +.gh-dashboard5-email .gh-dashboard5-chart-ticks { + padding: 28px 16px 0 0; +} + + +/* --------------------------------- +Dashboard v5 Section Engagement */ + +.gh-dashboard5-engagement { position: relative; } -.gh-dashboard5-anchor > .gh-dashboard5-box { - padding: 0; +.gh-dashboard5-engagement .prototype-selection { + top: 12px; } -.gh-dashboard5-stats { +.gh-dashboard5-engagement .gh-dashboard5-insert { display: flex; flex-direction: row; - justify-content: space-evenly; - width: 75%; - padding: 28px; -} - -.gh-dashboard5-stats > button { flex: 1; - border-left: 2px solid #D6EDF9; - padding: 2px 0 0 12px; - margin: 0 8px 0 0; - transition: all 100ms ease-out; - cursor: pointer; - display: block; - text-align: left; - padding-left: 13px; } -.gh-dashboard5-stats > button.is-selected { - border-color: #14b8ff; - border-width: 3px; - padding-left: 12px; -} - -.gh-dashboard5-chart { +.gh-dashboard5-engagement .gh-dashboard5-insert-item { + flex: 1; display: flex; flex-direction: row; - justify-content: stretch; - width: 100%; - height: 200px; + justify-content: flex-end; + min-height: 80px; +} + +.gh-dashboard5-engagement .gh-dashboard5-insert-item:nth-child(1) { + /* --- Engaged Over 30 Days */ + padding: 4px 16px 0 0; + border-right: 1px solid var(--whitegrey); +} + +.gh-dashboard5-engagement .gh-dashboard5-insert-item:nth-child(2) { + /* --- Engaged Over 7 Days */ + padding: 4px 16px 0 16px; +} + +.gh-dashboard5-split .gh-dashboard5-engagement .gh-dashboard5-insert { + flex-direction: column; +} + +.gh-dashboard5-split .gh-dashboard5-engagement .gh-dashboard5-insert-item { + border-radius: 0; + border-width: 0 0 1px; + padding: 8px 16px 16px 0; +} + +.gh-dashboard5-split .gh-dashboard5-engagement .gh-dashboard5-insert-item:nth-child(1) { + /* --- Engaged Over 30 Days */ + padding: 4px 16px 16px 0; + border-bottom: 1px solid var(--whitegrey); +} + +.gh-dashboard5-split .gh-dashboard5-engagement .gh-dashboard5-insert-item:nth-child(2) { + /* --- Engaged Over 7 Days */ + flex: 1; + display: flex; + flex-direction: column; + justify-content: flex-end; + padding: 16px 16px 0 0; +} + + +/* --------------------------------- +Dashboard v5 Section Posts */ + +.gh-dashboard5-posts .gh-dashboard5-box { + padding: 16px; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: stretch; +} + + +/* --------------------------------- +Dashboard v5 Section Activity */ + +.gh-dashboard5-activity .gh-dashboard5-box { + padding: 16px; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: stretch; }