mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 20:03:12 +03:00
Dashboard consolidated graph (#2323)
* Basic work to combine the three line graphs for the new dashboard refs: https://github.com/TryGhost/Team/issues/1467 - combined three line graphs into one main one at the top - still working on this, so some code is a little rough * Tidying up a few bits of consolidated graph in new dashboard refs: https://github.com/TryGhost/Team/issues/1467 * Updated chart anchor component for removed member counts no issue * Updated chart paid members to not reload on days change no refs * Moved did-insert to top element in chart-anchor * Fixed chart anchor to use filled member data * Replaced chart anchor divs with buttons * Tweaking up the paid graphs below anchor to improve visuals refs: https://github.com/TryGhost/Team/issues/1467 * Fixed missing type attributes on buttons in chart anchor * Updated MMR to MRR for the new consolidated graph refs: https://github.com/TryGhost/Team/issues/1467 * Added real MRR to chart anchor * Added open rate percentage data to chart email Co-authored-by: Simon Backx <simon@ghost.org>
This commit is contained in:
parent
da972bfe58
commit
8fe18d7e30
@ -1,12 +1,11 @@
|
||||
<section {{did-insert this.onInsert}}>
|
||||
|
||||
{{#if this.isLoading }}
|
||||
<GhLoadingSpinner />
|
||||
{{else}}
|
||||
{{#if this.areMembersEnabled}}
|
||||
<section class="gh-dashboard5-section gh-dashboard5-overview">
|
||||
<section class="gh-dashboard5-section gh-dashboard5-anchor">
|
||||
<article class="gh-dashboard5-box">
|
||||
<Dashboard::V5::ChartTotalMembers @days={{this.days}} />
|
||||
<Dashboard::V5::ChartAnchor @days={{this.days}} />
|
||||
</article>
|
||||
<div class="prototype-selection">
|
||||
<PowerSelect
|
||||
@ -29,14 +28,6 @@
|
||||
<div class="gh-dashboard5-paid">
|
||||
<section class="gh-dashboard5-section">
|
||||
<div class="gh-dashboard5-growth">
|
||||
<article class="gh-dashboard5-box">
|
||||
<Dashboard::V5::ChartMonthlyRevenue @days={{this.days}} />
|
||||
</article>
|
||||
|
||||
<article class="gh-dashboard5-box">
|
||||
<Dashboard::V5::ChartTotalPaid @days={{this.days}} />
|
||||
</article>
|
||||
|
||||
<article class="gh-dashboard5-box">
|
||||
<Dashboard::V5::ChartPaidMembers @days={{this.days}}/>
|
||||
</article>
|
||||
|
39
ghost/admin/app/components/dashboard/v5/chart-anchor.hbs
Normal file
39
ghost/admin/app/components/dashboard/v5/chart-anchor.hbs
Normal file
@ -0,0 +1,39 @@
|
||||
<div class="gh-dashboard5-stats" {{did-insert this.loadCharts}}>
|
||||
<button type="button" {{on "click" (fn this.changeChartDisplay "total")}} class={{if this.chartShowingTotal 'is-selected'}}>
|
||||
<div class="gh-dashboard5-number">
|
||||
{{format-number this.totalMembers}}
|
||||
{{#if this.hasTrends}}
|
||||
<Dashboard::v5::parts::ChartPercentage @percentage={{this.totalMembersTrend}}/>
|
||||
{{/if}}
|
||||
</div>
|
||||
<small class="gh-dashboard5-info">{{gh-pluralize this.totalMembers "Total member" without-count=true}}</small>
|
||||
</button>
|
||||
<button type="button" {{on "click" (fn this.changeChartDisplay "paid")}} class={{if this.chartShowingPaid 'is-selected'}}>
|
||||
<div class="gh-dashboard5-number">
|
||||
{{format-number this.paidMembers}}
|
||||
{{#if this.hasTrends}}
|
||||
<Dashboard::v5::parts::ChartPercentage @percentage={{this.paidMembersTrend}}/>
|
||||
{{/if}}
|
||||
</div>
|
||||
<small class="gh-dashboard5-info">{{gh-pluralize this.paidMembers "Paid member" without-count=true}}</small>
|
||||
</button>
|
||||
<button type="button" {{on "click" (fn this.changeChartDisplay "monthly")}} class={{if this.chartShowingMonthly 'is-selected'}}>
|
||||
<div class="gh-dashboard5-number">
|
||||
${{gh-price-amount this.currentMRR}}
|
||||
{{#if this.hasTrends}}
|
||||
<Dashboard::v5::parts::ChartPercentage @percentage={{this.mrrTrend}}/>
|
||||
{{/if}}
|
||||
</div>
|
||||
<small class="gh-dashboard5-info">Monthly revenue (MRR)</small>
|
||||
</button>
|
||||
</div>
|
||||
<div class="gh-dashboard5-chart">
|
||||
{{#if this.loading}}
|
||||
<div class="gh-dashboard5-loading" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}} />
|
||||
{{/if}}
|
||||
</div>
|
198
ghost/admin/app/components/dashboard/v5/chart-anchor.js
Normal file
198
ghost/admin/app/components/dashboard/v5/chart-anchor.js
Normal file
@ -0,0 +1,198 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
export default class ChartAnchor extends Component {
|
||||
@service dashboardStats;
|
||||
@tracked chartDisplay = 'total';
|
||||
|
||||
@action
|
||||
loadCharts() {
|
||||
this.dashboardStats.loadMemberCountStats();
|
||||
this.dashboardStats.loadMrrStats();
|
||||
}
|
||||
|
||||
@action
|
||||
changeChartDisplay(type) {
|
||||
this.chartDisplay = type;
|
||||
}
|
||||
|
||||
get chartShowingTotal() {
|
||||
return (this.chartDisplay === 'total');
|
||||
}
|
||||
|
||||
get chartShowingPaid() {
|
||||
return (this.chartDisplay === 'paid');
|
||||
}
|
||||
|
||||
get chartShowingMonthly() {
|
||||
return (this.chartDisplay === 'monthly');
|
||||
}
|
||||
|
||||
get loading() {
|
||||
if (this.chartDisplay === 'total') {
|
||||
return this.dashboardStats.memberCountStats === null;
|
||||
} else if (this.chartDisplay === 'paid') {
|
||||
return this.dashboardStats.memberCountStats === null;
|
||||
} else if (this.chartDisplay === 'monthly') {
|
||||
return this.dashboardStats.mrrStats === null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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 currentMRR() {
|
||||
return this.dashboardStats.currentMRR ?? 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);
|
||||
}
|
||||
|
||||
get mrrTrend() {
|
||||
return this.calculatePercentage(this.dashboardStats.currentMRRTrend, this.dashboardStats.currentMRR);
|
||||
}
|
||||
|
||||
get chartType() {
|
||||
return 'line';
|
||||
}
|
||||
|
||||
get chartData() {
|
||||
let stats = [];
|
||||
let labels = [];
|
||||
let data = [];
|
||||
|
||||
if (this.chartDisplay === 'total') {
|
||||
stats = this.dashboardStats.filledMemberCountStats;
|
||||
labels = stats.map(stat => stat.date);
|
||||
data = stats.map(stat => stat.paid + stat.free + stat.comped);
|
||||
}
|
||||
|
||||
if (this.chartDisplay === 'paid') {
|
||||
stats = this.dashboardStats.filledMemberCountStats;
|
||||
labels = stats.map(stat => stat.date);
|
||||
data = stats.map(stat => stat.paid);
|
||||
}
|
||||
|
||||
if (this.chartDisplay === 'monthly') {
|
||||
stats = this.dashboardStats.filledMrrStats;
|
||||
labels = stats.map(stat => stat.date);
|
||||
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 {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
title: {
|
||||
display: false
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
layout: {
|
||||
padding: {
|
||||
top: 20
|
||||
}
|
||||
},
|
||||
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'
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
calculatePercentage(from, to) {
|
||||
if (from === 0) {
|
||||
if (to > 0) {
|
||||
return 100;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.round((to - from) / from * 100);
|
||||
}
|
||||
}
|
@ -1,13 +1,9 @@
|
||||
<h3 {{did-insert this.loadCharts}}>Email</h3>
|
||||
<div class="gh-dashboard5-insert">
|
||||
<div class="gh-dashboard5-box">
|
||||
{{!-- <h4 class="gh-dashboard5-metric">Newsletter subscribers</h4> --}}
|
||||
{{!-- <div class="gh-dashboard5-number is-small">{{format-number this.dataSubscribers.total}}</div> --}}
|
||||
{{!-- <div class="gh-dashboard5-number is-small">{{format-number this.dataSubscribers.paid}} paid</div> --}}
|
||||
{{!-- <div class="gh-dashboard5-number is-small">{{format-number this.dataSubscribers.free}} free</div> --}}
|
||||
|
||||
<h4 class="gh-dashboard5-metric is-split">Email</h4>
|
||||
<div class="gh-dashboard5-number">{{format-number this.dataSubscribers.total}}</div>
|
||||
<div class="gh-dashboard5-number is-solo">
|
||||
{{format-number this.dataSubscribers.total}}<small><span class="gh-dashboard5-slash">/</span>{{format-number this.dataSubscribers.paid}} paid</small>
|
||||
</div>
|
||||
<small class="gh-dashboard5-info">Newsletter subscribers</small>
|
||||
</div>
|
||||
|
||||
@ -23,12 +19,11 @@
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
@options={{this.chartOptions}} />
|
||||
{{/if}}
|
||||
<div>
|
||||
<div class="gh-dashboard5-number">58%</div>
|
||||
<div class="gh-dashboard5-number">{{this.currentOpenRate}}%</div>
|
||||
<small class="gh-dashboard5-info">Open rate</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -25,6 +25,14 @@ export default class ChartEmailOpenRate extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
get currentOpenRate() {
|
||||
if (this.dashboardStats.emailOpenRateStats === null || this.dashboardStats.emailOpenRateStats.length === 0) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return this.dashboardStats.emailOpenRateStats[this.dashboardStats.emailOpenRateStats.length - 1].openRate;
|
||||
}
|
||||
|
||||
get dataEmailsSent() {
|
||||
return this.dashboardStats.emailsSent30d ?? 0;
|
||||
}
|
||||
@ -62,6 +70,8 @@ export default class ChartEmailOpenRate extends Component {
|
||||
|
||||
get chartOptions() {
|
||||
return {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
title: {
|
||||
display: false
|
||||
},
|
||||
|
@ -3,8 +3,7 @@
|
||||
<article class="gh-dashboard5-box">
|
||||
<div class="gh-dashboard5-insert">
|
||||
<div class="gh-dashboard5-box">
|
||||
<h4 class="gh-dashboard5-metric is-split">Engagement</h4>
|
||||
<div class="gh-dashboard5-number">{{this.data30Days}}</div>
|
||||
<div class="gh-dashboard5-number is-solo">{{this.data30Days}}</div>
|
||||
<small class="gh-dashboard5-info">Last 30 days</small>
|
||||
</div>
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
<h4
|
||||
class="gh-dashboard5-metric is-main"
|
||||
<small
|
||||
class="gh-dashboard5-info is-solo"
|
||||
{{did-insert this.loadCharts}}
|
||||
>
|
||||
Paid members
|
||||
</h4>
|
||||
>Paid members</small>
|
||||
|
||||
{{#if this.loading}}
|
||||
<div class="gh-dashboard5-loading" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
{{/if}}
|
||||
<div class="gh-dashboard5-chart">
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{150}} />
|
||||
</div>
|
||||
{{/if}}
|
||||
|
@ -53,6 +53,9 @@ export default class ChartPaidMembers extends Component {
|
||||
|
||||
get chartOptions() {
|
||||
return {
|
||||
animation: {
|
||||
duration: 0
|
||||
},
|
||||
title: {
|
||||
display: false
|
||||
},
|
||||
@ -105,8 +108,4 @@ export default class ChartPaidMembers extends Component {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get chartHeight() {
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
@ -17,14 +17,18 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<h4 class="gh-dashboard5-metric is-main" {{did-insert this.loadCharts}}>Paid mix</h4>
|
||||
<small
|
||||
class="gh-dashboard5-info is-solo"
|
||||
{{did-insert this.loadCharts}}
|
||||
>Paid mix</small>
|
||||
|
||||
{{#if this.loading}}
|
||||
<div class="gh-dashboard5-loading" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}}
|
||||
@height={{this.chartHeight}} />
|
||||
<div class="gh-dashboard5-chart">
|
||||
<EmberChart
|
||||
@type={{this.chartType}}
|
||||
@data={{this.chartData}}
|
||||
@options={{this.chartOptions}} />
|
||||
</div>
|
||||
{{/if}}
|
||||
|
@ -90,11 +90,12 @@ export default class ChartPaidMix extends Component {
|
||||
return {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: {
|
||||
duration: 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get chartHeight() {
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,3 @@
|
||||
{{!-- <h4
|
||||
class="gh-dashboard5-metric is-main"
|
||||
{{did-insert this.loadCharts}}
|
||||
{{did-update this.loadCharts @days}}
|
||||
>
|
||||
Total members
|
||||
</h4> --}}
|
||||
{{#if this.loading}}
|
||||
<div {{did-insert this.loadCharts}} class="gh-dashboard5-loading" style={{html-safe (concat "height: " this.chartHeight "px;")}}/>
|
||||
{{else}}
|
||||
|
@ -157,6 +157,15 @@ export default class DashboardStatsService extends Service {
|
||||
};
|
||||
}
|
||||
|
||||
get currentMRR() {
|
||||
if (!this.mrrStats) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stat = this.mrrStats[this.mrrStats.length - 1];
|
||||
return stat.mrr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {?MemberCounts}
|
||||
*/
|
||||
@ -192,6 +201,30 @@ export default class DashboardStatsService extends Service {
|
||||
};
|
||||
}
|
||||
|
||||
get currentMRRTrend() {
|
||||
if (!this.mrrStats) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.chartDays === 'all') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Search for the value at chartDays ago (if any, else the first before it, or the next one if not one before it)
|
||||
const searchDate = moment().add(-this.chartDays, 'days').format('YYYY-MM-DD');
|
||||
|
||||
for (let index = this.mrrStats.length - 1; index >= 0; index -= 1) {
|
||||
const stat = this.mrrStats[index];
|
||||
if (stat.date <= searchDate) {
|
||||
return stat.mrr;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't have any statistic from more than x days ago.
|
||||
// Return all zero values
|
||||
return 0;
|
||||
}
|
||||
|
||||
get filledMemberCountStats() {
|
||||
if (this.memberCountStats === null) {
|
||||
return null;
|
||||
|
@ -995,7 +995,7 @@ a.gh-dashboard-container {
|
||||
.prototype-paid-mix-dropdown {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 15px;
|
||||
top: 25px;
|
||||
}
|
||||
|
||||
.prototype-counts {
|
||||
@ -1093,15 +1093,16 @@ a.gh-dashboard-container {
|
||||
|
||||
.gh-dashboard5-number {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
align-items: flex-end;
|
||||
font-size: 3rem;
|
||||
line-height: 4rem;
|
||||
font-weight: 600;
|
||||
color: #15171a;
|
||||
color: var(--black);
|
||||
letter-spacing: -.1px;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
margin: 0 0 4px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.gh-dashboard5-number.is-small {
|
||||
@ -1109,7 +1110,17 @@ a.gh-dashboard-container {
|
||||
}
|
||||
|
||||
.gh-dashboard5-number.is-solo {
|
||||
margin-top: 46px;
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
.gh-dashboard5-number small {
|
||||
font-size: 1.4rem;
|
||||
padding: 0 0 0.25rem;
|
||||
color: var(--darkgrey);
|
||||
}
|
||||
|
||||
.gh-dashboard5-slash {
|
||||
margin: 0 0.5em 0 0;
|
||||
}
|
||||
|
||||
.gh-dashboard5-metric {
|
||||
@ -1157,6 +1168,10 @@ a.gh-dashboard-container {
|
||||
color: var(--midgrey);
|
||||
}
|
||||
|
||||
.gh-dashboard5-info.is-solo {
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
.gh-dashboard5-percentage {
|
||||
flex: 0;
|
||||
background: var(--whitegrey-d1);
|
||||
@ -1167,7 +1182,7 @@ a.gh-dashboard-container {
|
||||
letter-spacing: 0;
|
||||
color: var(--midgrey);
|
||||
padding: 2px 4px;
|
||||
margin: 5px 0 1px 0;
|
||||
margin: 5px 0 3px 0;
|
||||
}
|
||||
|
||||
.gh-dashboard5-percentage.is-positive {
|
||||
@ -1233,10 +1248,7 @@ a.gh-dashboard-container {
|
||||
} */
|
||||
|
||||
.gh-dashboard5-growth {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
grid-template-rows: auto auto;
|
||||
gap: 0;
|
||||
display: flex;
|
||||
margin-top: -25px;
|
||||
}
|
||||
|
||||
@ -1248,22 +1260,24 @@ a.gh-dashboard-container {
|
||||
} */
|
||||
|
||||
.gh-dashboard5-growth .gh-dashboard5-box:nth-child(1) {
|
||||
grid-column: 1 / span 2;
|
||||
grid-row: 1 / span 2;
|
||||
/* 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: 3 / span 2;
|
||||
grid-row: 1;
|
||||
/* 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) {
|
||||
/* .gh-dashboard5-growth .gh-dashboard5-box:nth-child(3) {
|
||||
grid-column: 3;
|
||||
grid-row: 2;
|
||||
border-radius: 0 0 0 3px;
|
||||
@ -1275,6 +1289,15 @@ a.gh-dashboard-container {
|
||||
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 {
|
||||
@ -1355,11 +1378,18 @@ a.gh-dashboard-container {
|
||||
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-left: 24px;
|
||||
padding-top: 24px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.gh-dashboard5-split .gh-dashboard5-engagement .gh-dashboard5-insert {
|
||||
@ -1375,12 +1405,18 @@ a.gh-dashboard-container {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1474,4 +1510,46 @@ a.gh-dashboard-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.gh-dashboard5-anchor {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gh-dashboard5-anchor > .gh-dashboard5-box {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.gh-dashboard5-stats {
|
||||
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 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user