listmonk/frontend/src/views/Dashboard.vue
2021-01-17 20:59:01 +05:30

232 lines
7.6 KiB
Vue

<template>
<section class="dashboard content">
<header class="columns">
<div class="column is-two-thirds">
<h1 class="title is-5">{{ dayjs().format("ddd, DD MMM") }}</h1>
</div>
</header>
<section class="counts wrap-small">
<div class="tile is-ancestor">
<div class="tile is-vertical is-12">
<div class="tile">
<div class="tile is-parent is-vertical relative">
<b-loading v-if="isCountsLoading" active :is-full-page="false" />
<article class="tile is-child notification">
<div class="columns is-mobile">
<div class="column is-6">
<p class="title">{{ $utils.niceNumber(counts.lists.total) }}</p>
<p class="is-size-6 has-text-grey">
{{ $tc('globals.terms.list', counts.lists.total) }}
</p>
</div>
<div class="column is-6">
<ul class="no is-size-7 has-text-grey">
<li>
<label>{{ $utils.niceNumber(counts.lists.public) }}</label>
{{ $t('lists.types.public') }}
</li>
<li>
<label>{{ $utils.niceNumber(counts.lists.private) }}</label>
{{ $t('lists.types.private') }}
</li>
<li>
<label>{{ $utils.niceNumber(counts.lists.optinSingle) }}</label>
{{ $t('lists.optins.single') }}
</li>
<li>
<label>{{ $utils.niceNumber(counts.lists.optinDouble) }}</label>
{{ $t('lists.optins.double') }}
</li>
</ul>
</div>
</div>
</article><!-- lists -->
<article class="tile is-child notification">
<div class="columns is-mobile">
<div class="column is-6">
<p class="title">{{ $utils.niceNumber(counts.campaigns.total) }}</p>
<p class="is-size-6 has-text-grey">
{{ $tc('globals.terms.campaign', counts.campaigns.total) }}
</p>
</div>
<div class="column is-6">
<ul class="no is-size-7 has-text-grey">
<li v-for="(num, status) in counts.campaigns.byStatus" :key="status">
<label>{{ num }}</label> {{ status }}
</li>
</ul>
</div>
</div>
</article><!-- campaigns -->
</div><!-- block -->
<div class="tile is-parent relative">
<b-loading v-if="isCountsLoading" active :is-full-page="false" />
<article class="tile is-child notification">
<div class="columns is-mobile">
<div class="column is-6">
<p class="title">{{ $utils.niceNumber(counts.subscribers.total) }}</p>
<p class="is-size-6 has-text-grey">
{{ $tc('globals.terms.subscriber', counts.subscribers.total) }}
</p>
</div>
<div class="column is-6">
<ul class="no is-size-7 has-text-grey">
<li>
<label>{{ $utils.niceNumber(counts.subscribers.blocklisted) }}</label>
{{ $t('subscribers.status.blocklisted') }}
</li>
<li>
<label>{{ $utils.niceNumber(counts.subscribers.orphans) }}</label>
{{ $t('dashboard.orphanSubs') }}
</li>
</ul>
</div><!-- subscriber breakdown -->
</div><!-- subscriber columns -->
<hr />
<div class="columns">
<div class="column is-6">
<p class="title">{{ $utils.niceNumber(counts.messages) }}</p>
<p class="is-size-6 has-text-grey">
{{ $t('dashboard.messagesSent') }}
</p>
</div>
</div>
</article><!-- subscribers -->
</div>
</div>
<div class="tile is-parent relative">
<b-loading v-if="isChartsLoading" active :is-full-page="false" />
<article class="tile is-child notification charts">
<div class="columns">
<div class="column is-6">
<h3 class="title is-size-6">{{ $t('dashboard.campaignViews') }}</h3><br />
<vue-c3 v-if="chartViewsInst" :handler="chartViewsInst"></vue-c3>
<empty-placeholder v-else-if="!isChartsLoading" />
</div>
<div class="column is-6">
<h3 class="title is-size-6 has-text-right">
{{ $t('dashboard.linkClicks') }}
</h3><br />
<vue-c3 v-if="chartClicksInst" :handler="chartClicksInst"></vue-c3>
<empty-placeholder v-else-if="!isChartsLoading" />
</div>
</div>
</article>
</div>
</div>
</div><!-- tile block -->
</section>
</section>
</template>
<style lang="css">
@import "~c3/c3.css";
</style>
<script>
import Vue from 'vue';
import VueC3 from 'vue-c3';
import dayjs from 'dayjs';
import { colors } from '../constants';
import EmptyPlaceholder from '../components/EmptyPlaceholder.vue';
export default Vue.extend({
components: {
EmptyPlaceholder,
VueC3,
},
data() {
return {
// Unique Vue() instances for each chart.
chartViewsInst: null,
chartClicksInst: null,
isChartsLoading: true,
isCountsLoading: true,
counts: {
lists: {},
subscribers: {},
campaigns: {},
messages: 0,
},
};
},
methods: {
makeChart(label, data) {
const conf = {
data: {
columns: [
[label, ...data.map((d) => d.count).reverse()],
],
type: 'spline',
color() {
return colors.primary;
},
},
axis: {
x: {
type: 'category',
categories: data.map((d) => dayjs(d.date).format('DD MMM')).reverse(),
tick: {
rotate: -45,
multiline: false,
culling: { max: 10 },
},
},
},
legend: {
show: false,
},
};
return conf;
},
},
computed: {
dayjs() {
return dayjs;
},
},
mounted() {
// Pull the counts.
this.$api.getDashboardCounts().then((data) => {
this.counts = data;
this.isCountsLoading = false;
});
// Pull the charts.
this.$api.getDashboardCharts().then((data) => {
this.isChartsLoading = false;
// vue-c3 lib requires unique instances of Vue() to communicate.
if (data.campaignViews.length > 0) {
this.chartViewsInst = this;
this.$nextTick(() => {
this.chartViewsInst.$emit('init',
this.makeChart(this.$t('dashboard.campaignViews'), data.campaignViews));
});
}
if (data.linkClicks.length > 0) {
this.chartClicksInst = new Vue();
this.$nextTick(() => {
this.chartClicksInst.$emit('init',
this.makeChart(this.$t('dashboard.linkClicks'), data.linkClicks));
});
}
});
},
});
</script>