Upgrade Vue and Buefy UI lib.

- Vue 2.6 introduces "v-slot" which Buefy 0.9.7 uses.
- Refactor all `<b-table>` and `<b-column>` instances to work with the
  new `v-slot` snytax.
- Refactor `<b-column>` <td> and class attributes to work wit hthe new
  syntax.
- Fix Buefy scss setup to work with the update.
- Fix sidebar responsive view to work with the update.
This commit is contained in:
Kailash Nadh 2021-05-23 14:32:56 +05:30
parent c593be51c1
commit dea4d185ae
9 changed files with 386 additions and 426 deletions

View file

@ -10,7 +10,7 @@
},
"dependencies": {
"axios": "^0.21.1",
"buefy": "^0.8.20",
"buefy": "^0.9.7",
"c3": "^0.7.18",
"codeflask": "^1.4.1",
"core-js": "^3.6.5",

View file

@ -1,44 +0,0 @@
@import "~bulma/sass/base/_all";
@import "~bulma/sass/elements/_all";
@import "~bulma/sass/components/card";
@import "~bulma/sass/components/dropdown";
@import "~bulma/sass/components/level";
@import "~bulma/sass/components/menu";
@import "~bulma/sass/components/message";
@import "~bulma/sass/components/modal";
@import "~bulma/sass/components/navbar";
@import "~bulma/sass/components/pagination";
@import "~bulma/sass/components/tabs";
@import "~bulma/sass/form/_all";
@import "~bulma/sass/grid/columns";
@import "~bulma/sass/grid/tiles";
@import "~bulma/sass/layout/section";
@import "~bulma/sass/layout/footer";
@import "~buefy/src/scss/utils/_all";
@import "~buefy/src/scss/components/_autocomplete";
@import "~buefy/src/scss/components/_carousel";
@import "~buefy/src/scss/components/_checkbox";
@import "~buefy/src/scss/components/_datepicker";
@import "~buefy/src/scss/components/_dialog";
@import "~buefy/src/scss/components/_dropdown";
@import "~buefy/src/scss/components/_form";
@import "~buefy/src/scss/components/_icon";
@import "~buefy/src/scss/components/_loading";
@import "~buefy/src/scss/components/_menu";
@import "~buefy/src/scss/components/_message";
@import "~buefy/src/scss/components/_modal";
@import "~buefy/src/scss/components/_pagination";
@import "~buefy/src/scss/components/_notices";
@import "~buefy/src/scss/components/_progress";
@import "~buefy/src/scss/components/_radio";
@import "~buefy/src/scss/components/_select";
@import "~buefy/src/scss/components/_sidebar";
@import "~buefy/src/scss/components/_switch";
@import "~buefy/src/scss/components/_table";
@import "~buefy/src/scss/components/_tabs";
@import "~buefy/src/scss/components/_tag";
@import "~buefy/src/scss/components/_taginput";
@import "~buefy/src/scss/components/_timepicker";
@import "~buefy/src/scss/components/_tooltip";
@import "~buefy/src/scss/components/_upload";

View file

@ -30,10 +30,11 @@ $modal-background-background-color: rgba(0, 0, 0, .30);
$speed-slow: 25ms !default;
$speed-slower: 50ms !default;
/* Import full Bulma and Buefy to override styles. */
// @import "~bulma";
@import "./buefy";
/* Import full Bulma and Buefy */
@import "~bulma";
@import "~buefy/src/scss/buefy";
/* Custom style overrides */
html, body {
height: 100%;
}
@ -752,6 +753,7 @@ section.campaign {
/* Hide sidebar menu captions on mobile */
.b-sidebar .sidebar-content.is-mini-mobile {
max-width: 90px;
.menu-list {
li {
margin-bottom: 30px;
@ -773,7 +775,6 @@ section.campaign {
td .tags {
display: block;
text-align: right;
.tag:not(:last-child) {
margin-right: 0;

View file

@ -118,4 +118,8 @@ export default class Utils {
duration: duration || 2000,
});
};
// Takes a props.row from a Buefy b-column <td> template and
// returns a `data-id` attribute which Buefy then applies to the td.
tdID = (row) => ({ 'data-id': row.id.toString() });
}

View file

@ -29,175 +29,173 @@
paginated backend-pagination pagination-position="both" @page-change="onPageChange"
:current-page="queryParams.page" :per-page="campaigns.perPage" :total="campaigns.total"
hoverable backend-sorting @sort="onSort">
<template slot-scope="props">
<b-table-column class="status" field="status" :label="$t('globals.fields.status')"
width="10%" :id="props.row.id" sortable
header-class="cy-status" :data-id="props.row.id">
<div>
<p>
<router-link :to="{ name: 'campaign', params: { 'id': props.row.id }}">
<b-tag :class="props.row.status">
{{ $t(`campaigns.status.${props.row.status}`) }}
</b-tag>
<span class="spinner is-tiny" v-if="isRunning(props.row.id)">
<b-loading :is-full-page="false" active />
</span>
</router-link>
</p>
<p v-if="isSheduled(props.row)">
<b-tooltip :label="$t('scheduled')" type="is-dark">
<span class="is-size-7 has-text-grey scheduled">
<b-icon icon="alarm" size="is-small" />
{{ $utils.duration(Date(), props.row.sendAt, true) }}
<br />{{ $utils.niceDate(props.row.sendAt, true) }}
</span>
</b-tooltip>
</p>
</div>
</b-table-column>
<b-table-column field="name" :label="$t('globals.fields.name')" sortable width="25%"
header-class="cy-name">
<div>
<p>
<b-tag v-if="props.row.type !== 'regular'" class="is-small">
{{ props.row.type }}
</b-tag>
<router-link :to="{ name: 'campaign', params: { 'id': props.row.id }}">
{{ props.row.name }}</router-link>
</p>
<p class="is-size-7 has-text-grey">{{ props.row.subject }}</p>
<b-taglist>
<b-tag class="is-small" v-for="t in props.row.tags" :key="t">{{ t }}</b-tag>
</b-taglist>
</div>
</b-table-column>
<b-table-column class="lists" field="lists"
:label="$t('globals.terms.lists')" width="15%">
<ul class="no">
<li v-for="l in props.row.lists" :key="l.id">
<router-link :to="{name: 'subscribers_list', params: { listID: l.id }}">
{{ l.name }}
</router-link>
</li>
</ul>
</b-table-column>
<b-table-column field="created_at" :label="$t('campaigns.timestamps')"
width="19%" sortable header-class="cy-timestamp">
<div class="fields timestamps" :set="stats = getCampaignStats(props.row)">
<p>
<label>{{ $t('globals.fields.createdAt') }}</label>
{{ $utils.niceDate(props.row.createdAt, true) }}
</p>
<p v-if="stats.startedAt">
<label>{{ $t('campaigns.startedAt') }}</label>
{{ $utils.niceDate(stats.startedAt, true) }}
</p>
<p v-if="isDone(props.row)">
<label>{{ $t('campaigns.ended') }}</label>
{{ $utils.niceDate(stats.updatedAt, true) }}
</p>
<p v-if="stats.startedAt && stats.updatedAt"
class="is-capitalized" title="Duration">
<label><b-icon icon="alarm" size="is-small" /></label>
{{ $utils.duration(stats.startedAt, stats.updatedAt) }}
</p>
</div>
</b-table-column>
<b-table-column v-slot="props" class="status" field="status"
:label="$t('globals.fields.status')" width="10%" sortable
:td-attrs="$utils.tdID" header-class="cy-status">
<div>
<p>
<router-link :to="{ name: 'campaign', params: { 'id': props.row.id }}">
<b-tag :class="props.row.status">
{{ $t(`campaigns.status.${props.row.status}`) }}
</b-tag>
<span class="spinner is-tiny" v-if="isRunning(props.row.id)">
<b-loading :is-full-page="false" active />
</span>
</router-link>
</p>
<p v-if="isSheduled(props.row)">
<b-tooltip :label="$t('scheduled')" type="is-dark">
<span class="is-size-7 has-text-grey scheduled">
<b-icon icon="alarm" size="is-small" />
{{ $utils.duration(Date(), props.row.sendAt, true) }}
<br />{{ $utils.niceDate(props.row.sendAt, true) }}
</span>
</b-tooltip>
</p>
</div>
</b-table-column>
<b-table-column v-slot="props" field="name" :label="$t('globals.fields.name')" width="25%"
sortable header-class="cy-name">
<div>
<p>
<b-tag v-if="props.row.type !== 'regular'" class="is-small">
{{ props.row.type }}
</b-tag>
<router-link :to="{ name: 'campaign', params: { 'id': props.row.id }}">
{{ props.row.name }}</router-link>
</p>
<p class="is-size-7 has-text-grey">{{ props.row.subject }}</p>
<b-taglist>
<b-tag class="is-small" v-for="t in props.row.tags" :key="t">{{ t }}</b-tag>
</b-taglist>
</div>
</b-table-column>
<b-table-column v-slot="props" class="lists" field="lists"
:label="$t('globals.terms.lists')" width="15%">
<ul class="no">
<li v-for="l in props.row.lists" :key="l.id">
<router-link :to="{name: 'subscribers_list', params: { listID: l.id }}">
{{ l.name }}
</router-link>
</li>
</ul>
</b-table-column>
<b-table-column v-slot="props" field="created_at" :label="$t('campaigns.timestamps')"
width="19%" sortable header-class="cy-timestamp">
<div class="fields timestamps" :set="stats = getCampaignStats(props.row)">
<p>
<label>{{ $t('globals.fields.createdAt') }}</label>
{{ $utils.niceDate(props.row.createdAt, true) }}
</p>
<p v-if="stats.startedAt">
<label>{{ $t('campaigns.startedAt') }}</label>
{{ $utils.niceDate(stats.startedAt, true) }}
</p>
<p v-if="isDone(props.row)">
<label>{{ $t('campaigns.ended') }}</label>
{{ $utils.niceDate(stats.updatedAt, true) }}
</p>
<p v-if="stats.startedAt && stats.updatedAt"
class="is-capitalized" title="Duration">
<label><b-icon icon="alarm" size="is-small" /></label>
{{ $utils.duration(stats.startedAt, stats.updatedAt) }}
</p>
</div>
</b-table-column>
<b-table-column field="stats" :class="props.row.status"
:label="$t('campaigns.stats')" width="18%">
<div class="fields stats" :set="stats = getCampaignStats(props.row)">
<p>
<label>{{ $t('campaigns.views') }}</label>
{{ props.row.views }}
</p>
<p>
<label>{{ $t('campaigns.clicks') }}</label>
{{ props.row.clicks }}
</p>
<p>
<label>{{ $t('campaigns.sent') }}</label>
{{ stats.sent }} / {{ stats.toSend }}
</p>
<p title="Speed" v-if="stats.rate">
<label><b-icon icon="speedometer" size="is-small"></b-icon></label>
<span class="send-rate">
{{ stats.rate.toFixed(0) }} / min
</span>
</p>
<p v-if="isRunning(props.row.id)">
<label>{{ $t('campaigns.progress') }}
<span class="spinner is-tiny">
<b-loading :is-full-page="false" active />
</span>
</label>
<b-progress :value="stats.sent / stats.toSend * 100" size="is-small" />
</p>
</div>
</b-table-column>
<b-table-column v-slot="props" field="stats" :label="$t('campaigns.stats')" width="18%">
<div class="fields stats" :set="stats = getCampaignStats(props.row)">
<p>
<label>{{ $t('campaigns.views') }}</label>
{{ props.row.views }}
</p>
<p>
<label>{{ $t('campaigns.clicks') }}</label>
{{ props.row.clicks }}
</p>
<p>
<label>{{ $t('campaigns.sent') }}</label>
{{ stats.sent }} / {{ stats.toSend }}
</p>
<p title="Speed" v-if="stats.rate">
<label><b-icon icon="speedometer" size="is-small"></b-icon></label>
<span class="send-rate">
{{ stats.rate.toFixed(0) }} / min
</span>
</p>
<p v-if="isRunning(props.row.id)">
<label>{{ $t('campaigns.progress') }}
<span class="spinner is-tiny">
<b-loading :is-full-page="false" active />
</span>
</label>
<b-progress :value="stats.sent / stats.toSend * 100" size="is-small" />
</p>
</div>
</b-table-column>
<b-table-column class="actions" width="13%" align="right">
<div>
<a href="" v-if="canStart(props.row)"
@click.prevent="$utils.confirm(null,
() => changeCampaignStatus(props.row, 'running'))" data-cy="btn-start">
<b-tooltip :label="$t('campaigns.start')" type="is-dark">
<b-icon icon="rocket-launch-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" v-if="canPause(props.row)"
@click.prevent="$utils.confirm(null,
() => changeCampaignStatus(props.row, 'paused'))" data-cy="btn-pause">
<b-tooltip :label="$t('campaigns.pause')" type="is-dark">
<b-icon icon="pause-circle-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" v-if="canResume(props.row)"
@click.prevent="$utils.confirm(null,
() => changeCampaignStatus(props.row, 'running'))" data-cy="btn-resume">
<b-tooltip :label="$t('campaigns.send')" type="is-dark">
<b-icon icon="rocket-launch-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" v-if="canSchedule(props.row)"
@click.prevent="$utils.confirm($t('campaigns.confirmSchedule'),
() => changeCampaignStatus(props.row, 'scheduled'))" data-cy="btn-schedule">
<b-tooltip :label="$t('campaigns.schedule')" type="is-dark">
<b-icon icon="clock-start" size="is-small" />
</b-tooltip>
</a>
<a href="" @click.prevent="previewCampaign(props.row)" data-cy="btn-preview">
<b-tooltip :label="$t('campaigns.preview')" type="is-dark">
<b-icon icon="file-find-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" @click.prevent="$utils.prompt($t('globals.buttons.clone'),
{ placeholder: $t('globals.fields.name'),
value: $t('campaigns.copyOf', { name: props.row.name }) },
(name) => cloneCampaign(name, props.row))"
data-cy="btn-clone">
<b-tooltip :label="$t('globals.buttons.clone')" type="is-dark">
<b-icon icon="file-multiple-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" v-if="canCancel(props.row)"
@click.prevent="$utils.confirm(null,
() => changeCampaignStatus(props.row, 'cancelled'))"
data-cy="btn-cancel">
<b-tooltip :label="$t('globals.buttons.cancel')" type="is-dark">
<b-icon icon="cancel" size="is-small" />
</b-tooltip>
</a>
<a href="" @click.prevent="$utils.confirm($tc('campaigns.confirmDelete'),
() => deleteCampaign(props.row))" data-cy="btn-delete">
<b-icon icon="trash-can-outline" size="is-small" />
</a>
</div>
</b-table-column>
</template>
<template slot="empty" v-if="!loading.campaigns">
<empty-placeholder />
</template>
<b-table-column v-slot="props" cell-class="actions" width="13%" align="right">
<div>
<a href="" v-if="canStart(props.row)"
@click.prevent="$utils.confirm(null,
() => changeCampaignStatus(props.row, 'running'))" data-cy="btn-start">
<b-tooltip :label="$t('campaigns.start')" type="is-dark">
<b-icon icon="rocket-launch-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" v-if="canPause(props.row)"
@click.prevent="$utils.confirm(null,
() => changeCampaignStatus(props.row, 'paused'))" data-cy="btn-pause">
<b-tooltip :label="$t('campaigns.pause')" type="is-dark">
<b-icon icon="pause-circle-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" v-if="canResume(props.row)"
@click.prevent="$utils.confirm(null,
() => changeCampaignStatus(props.row, 'running'))" data-cy="btn-resume">
<b-tooltip :label="$t('campaigns.send')" type="is-dark">
<b-icon icon="rocket-launch-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" v-if="canSchedule(props.row)"
@click.prevent="$utils.confirm($t('campaigns.confirmSchedule'),
() => changeCampaignStatus(props.row, 'scheduled'))" data-cy="btn-schedule">
<b-tooltip :label="$t('campaigns.schedule')" type="is-dark">
<b-icon icon="clock-start" size="is-small" />
</b-tooltip>
</a>
<a href="" @click.prevent="previewCampaign(props.row)" data-cy="btn-preview">
<b-tooltip :label="$t('campaigns.preview')" type="is-dark">
<b-icon icon="file-find-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" @click.prevent="$utils.prompt($t('globals.buttons.clone'),
{ placeholder: $t('globals.fields.name'),
value: $t('campaigns.copyOf', { name: props.row.name }) },
(name) => cloneCampaign(name, props.row))"
data-cy="btn-clone">
<b-tooltip :label="$t('globals.buttons.clone')" type="is-dark">
<b-icon icon="file-multiple-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" v-if="canCancel(props.row)"
@click.prevent="$utils.confirm(null,
() => changeCampaignStatus(props.row, 'cancelled'))"
data-cy="btn-cancel">
<b-tooltip :label="$t('globals.buttons.cancel')" type="is-dark">
<b-icon icon="cancel" size="is-small" />
</b-tooltip>
</a>
<a href="" @click.prevent="$utils.confirm($tc('campaigns.confirmDelete'),
() => deleteCampaign(props.row))" data-cy="btn-delete">
<b-icon icon="trash-can-outline" size="is-small" />
</a>
</div>
</b-table-column>
<template #empty v-if="!loading.campaigns">
<empty-placeholder />
</template>
</b-table>
<campaign-preview v-if="previewItem"

View file

@ -22,84 +22,85 @@
:current-page="queryParams.page" :per-page="lists.perPage" :total="lists.total"
backend-sorting @sort="onSort"
>
<template slot-scope="props">
<b-table-column field="name" :label="$t('globals.fields.name')" header-class="cy-name"
sortable width="25%" paginated backend-pagination pagination-position="both"
@page-change="onPageChange" :data-id="props.row.id">
<div>
<router-link :to="{name: 'subscribers_list', params: { listID: props.row.id }}">
{{ props.row.name }}
</router-link>
<b-taglist>
<b-tag class="is-small" v-for="t in props.row.tags" :key="t">{{ t }}</b-tag>
</b-taglist>
</div>
</b-table-column>
<b-table-column v-slot="props" field="name" :label="$t('globals.fields.name')"
header-class="cy-name" sortable width="25%"
paginated backend-pagination pagination-position="both"
:td-attrs="$utils.tdID"
@page-change="onPageChange">
<div>
<router-link :to="{name: 'subscribers_list', params: { listID: props.row.id }}">
{{ props.row.name }}
</router-link>
<b-taglist>
<b-tag class="is-small" v-for="t in props.row.tags" :key="t">{{ t }}</b-tag>
</b-taglist>
</div>
</b-table-column>
<b-table-column field="type" :label="$t('globals.fields.type')" header-class="cy-type"
sortable>
<div>
<b-tag :class="props.row.type" :data-cy="`type-${props.row.type}`">
{{ $t('lists.types.' + props.row.type) }}
</b-tag>
{{ ' ' }}
<b-tag :data-cy="`optin-${props.row.optin}`">
<b-icon :icon="props.row.optin === 'double' ?
'account-check-outline' : 'account-off-outline'" size="is-small" />
{{ ' ' }}
{{ $t('lists.optins.' + props.row.optin) }}
</b-tag>{{ ' ' }}
<a v-if="props.row.optin === 'double'" class="is-size-7 send-optin"
href="#" @click="$utils.confirm(null, () => createOptinCampaign(props.row))"
data-cy="btn-send-optin-campaign">
<b-tooltip :label="$t('lists.sendOptinCampaign')" type="is-dark">
<b-icon icon="rocket-launch-outline" size="is-small" />
{{ $t('lists.sendOptinCampaign') }}
</b-tooltip>
</a>
</div>
</b-table-column>
<b-table-column v-slot="props" field="type" :label="$t('globals.fields.type')"
header-class="cy-type" sortable>
<div>
<b-tag :class="props.row.type" :data-cy="`type-${props.row.type}`">
{{ $t('lists.types.' + props.row.type) }}
</b-tag>
{{ ' ' }}
<b-tag :data-cy="`optin-${props.row.optin}`">
<b-icon :icon="props.row.optin === 'double' ?
'account-check-outline' : 'account-off-outline'" size="is-small" />
{{ ' ' }}
{{ $t('lists.optins.' + props.row.optin) }}
</b-tag>{{ ' ' }}
<a v-if="props.row.optin === 'double'" class="is-size-7 send-optin"
href="#" @click="$utils.confirm(null, () => createOptinCampaign(props.row))"
data-cy="btn-send-optin-campaign">
<b-tooltip :label="$t('lists.sendOptinCampaign')" type="is-dark">
<b-icon icon="rocket-launch-outline" size="is-small" />
{{ $t('lists.sendOptinCampaign') }}
</b-tooltip>
</a>
</div>
</b-table-column>
<b-table-column field="subscriber_count" :label="$t('globals.terms.subscribers')"
header-class="cy-subscribers" numeric sortable centered>
<router-link :to="`/subscribers/lists/${props.row.id}`">
{{ props.row.subscriberCount }}
</router-link>
</b-table-column>
<b-table-column v-slot="props" field="subscriber_count"
:label="$t('globals.terms.subscribers')" header-class="cy-subscribers"
numeric sortable centered>
<router-link :to="`/subscribers/lists/${props.row.id}`">
{{ props.row.subscriberCount }}
</router-link>
</b-table-column>
<b-table-column field="created_at" :label="$t('globals.fields.createdAt')"
header-class="cy-created_at" sortable>
{{ $utils.niceDate(props.row.createdAt) }}
</b-table-column>
<b-table-column field="updated_at" :label="$t('globals.fields.updatedAt')"
header-class="cy-updated_at" sortable>
{{ $utils.niceDate(props.row.updatedAt) }}
</b-table-column>
<b-table-column v-slot="props" field="created_at" :label="$t('globals.fields.createdAt')"
header-class="cy-created_at" sortable>
{{ $utils.niceDate(props.row.createdAt) }}
</b-table-column>
<b-table-column v-slot="props" field="updated_at" :label="$t('globals.fields.updatedAt')"
header-class="cy-updated_at" sortable>
{{ $utils.niceDate(props.row.updatedAt) }}
</b-table-column>
<b-table-column class="actions" align="right">
<div>
<router-link :to="`/campaigns/new?list_id=${props.row.id}`" data-cy="btn-campaign">
<b-tooltip :label="$t('lists.sendCampaign')" type="is-dark">
<b-icon icon="rocket-launch-outline" size="is-small" />
</b-tooltip>
</router-link>
<a href="" @click.prevent="showEditForm(props.row)" data-cy="btn-edit">
<b-tooltip :label="$t('globals.buttons.edit')" type="is-dark">
<b-icon icon="pencil-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" @click.prevent="deleteList(props.row)" data-cy="btn-delete">
<b-tooltip :label="$t('globals.buttons.delete')" type="is-dark">
<b-icon icon="trash-can-outline" size="is-small" />
</b-tooltip>
</a>
</div>
</b-table-column>
</template>
<b-table-column v-slot="props" cell-class="actions" align="right">
<div>
<router-link :to="`/campaigns/new?list_id=${props.row.id}`" data-cy="btn-campaign">
<b-tooltip :label="$t('lists.sendCampaign')" type="is-dark">
<b-icon icon="rocket-launch-outline" size="is-small" />
</b-tooltip>
</router-link>
<a href="" @click.prevent="showEditForm(props.row)" data-cy="btn-edit">
<b-tooltip :label="$t('globals.buttons.edit')" type="is-dark">
<b-icon icon="pencil-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" @click.prevent="deleteList(props.row)" data-cy="btn-delete">
<b-tooltip :label="$t('globals.buttons.delete')" type="is-dark">
<b-icon icon="trash-can-outline" size="is-small" />
</b-tooltip>
</a>
</div>
</b-table-column>
<template slot="empty" v-if="!loading.lists">
<empty-placeholder />
</template>
<template #empty v-if="!loading.lists">
<empty-placeholder />
</template>
</b-table>
<!-- Add / edit form modal -->

View file

@ -108,86 +108,85 @@
paginated backend-pagination pagination-position="both" @page-change="onPageChange"
:current-page="queryParams.page" :per-page="subscribers.perPage" :total="subscribers.total"
hoverable checkable backend-sorting @sort="onSort">
<template slot="top-left">
<template #top-left>
<a href='' @click.prevent="exportSubscribers">
<b-icon icon="cloud-download-outline" size="is-small" /> {{ $t('subscribers.export') }}
</a>
</template>
<template slot-scope="props">
<b-table-column field="status" :label="$t('globals.fields.status')"
header-class="cy-status" :data-id="props.row.id" sortable>
<a :href="`/subscribers/${props.row.id}`"
@click.prevent="showEditForm(props.row)">
<b-tag :class="props.row.status">
{{ $t('subscribers.status.'+ props.row.status) }}
<b-table-column v-slot="props" field="status" :label="$t('globals.fields.status')"
header-class="cy-status" :td-attrs="$utils.tdID" sortable>
<a :href="`/subscribers/${props.row.id}`"
@click.prevent="showEditForm(props.row)">
<b-tag :class="props.row.status">
{{ $t('subscribers.status.'+ props.row.status) }}
</b-tag>
</a>
</b-table-column>
<b-table-column v-slot="props" field="email" :label="$t('subscribers.email')"
header-class="cy-email" sortable>
<a :href="`/subscribers/${props.row.id}`"
@click.prevent="showEditForm(props.row)">
{{ props.row.email }}
</a>
<b-taglist>
<template v-for="l in props.row.lists">
<router-link :to="`/subscribers/lists/${l.id}`"
v-bind:key="l.id" style="padding-right:0.5em;">
<b-tag :class="l.subscriptionStatus" size="is-small" :key="l.id">
{{ l.name }}
<sup>{{ $t('subscribers.status.'+ l.subscriptionStatus) }}</sup>
</b-tag>
</a>
</b-table-column>
</router-link>
</template>
</b-taglist>
</b-table-column>
<b-table-column field="email" :label="$t('subscribers.email')"
header-class="cy-email" sortable>
<a :href="`/subscribers/${props.row.id}`"
@click.prevent="showEditForm(props.row)">
{{ props.row.email }}
</a>
<b-taglist>
<template v-for="l in props.row.lists">
<router-link :to="`/subscribers/lists/${l.id}`"
v-bind:key="l.id" style="padding-right:0.5em;">
<b-tag :class="l.subscriptionStatus" size="is-small" :key="l.id">
{{ l.name }}
<sup>{{ $t('subscribers.status.'+ l.subscriptionStatus) }}</sup>
</b-tag>
</router-link>
</template>
</b-taglist>
</b-table-column>
<b-table-column v-slot="props" field="name" :label="$t('globals.fields.name')"
header-class="cy-name" sortable>
<a :href="`/subscribers/${props.row.id}`"
@click.prevent="showEditForm(props.row)">
{{ props.row.name }}
</a>
</b-table-column>
<b-table-column field="name" :label="$t('globals.fields.name')"
header-class="cy-name" sortable>
<a :href="`/subscribers/${props.row.id}`"
@click.prevent="showEditForm(props.row)">
{{ props.row.name }}
</a>
</b-table-column>
<b-table-column v-slot="props" field="lists" :label="$t('globals.terms.lists')"
header-class="cy-lists" centered>
{{ listCount(props.row.lists) }}
</b-table-column>
<b-table-column field="lists" :label="$t('globals.terms.lists')"
header-class="cy-lists" numeric centered>
{{ listCount(props.row.lists) }}
</b-table-column>
<b-table-column v-slot="props" field="created_at" :label="$t('globals.fields.createdAt')"
header-class="cy-created_at" sortable>
{{ $utils.niceDate(props.row.createdAt) }}
</b-table-column>
<b-table-column field="created_at" :label="$t('globals.fields.createdAt')"
header-class="cy-created_at" sortable>
{{ $utils.niceDate(props.row.createdAt) }}
</b-table-column>
<b-table-column v-slot="props" field="updated_at" :label="$t('globals.fields.updatedAt')"
header-class="cy-updated_at" sortable>
{{ $utils.niceDate(props.row.updatedAt) }}
</b-table-column>
<b-table-column field="updated_at" :label="$t('globals.fields.updatedAt')"
header-class="cy-updated_at" sortable>
{{ $utils.niceDate(props.row.updatedAt) }}
</b-table-column>
<b-table-column v-slot="props" label="Actions" cell-class="actions" align="right">
<div>
<a :href="`/api/subscribers/${props.row.id}/export`" data-cy="btn-download">
<b-tooltip :label="$t('subscribers.downloadData')" type="is-dark">
<b-icon icon="cloud-download-outline" size="is-small" />
</b-tooltip>
</a>
<a :href="`/subscribers/${props.row.id}`"
@click.prevent="showEditForm(props.row)" data-cy="btn-edit">
<b-tooltip :label="$t('globals.buttons.edit')" type="is-dark">
<b-icon icon="pencil-outline" size="is-small" />
</b-tooltip>
</a>
<a href='' @click.prevent="deleteSubscriber(props.row)" data-cy="btn-delete">
<b-tooltip :label="$t('globals.buttons.delete')" type="is-dark">
<b-icon icon="trash-can-outline" size="is-small" />
</b-tooltip>
</a>
</div>
</b-table-column>
<b-table-column class="actions" align="right">
<div>
<a :href="`/api/subscribers/${props.row.id}/export`" data-cy="btn-download">
<b-tooltip :label="$t('subscribers.downloadData')" type="is-dark">
<b-icon icon="cloud-download-outline" size="is-small" />
</b-tooltip>
</a>
<a :href="`/subscribers/${props.row.id}`"
@click.prevent="showEditForm(props.row)" data-cy="btn-edit">
<b-tooltip :label="$t('globals.buttons.edit')" type="is-dark">
<b-icon icon="pencil-outline" size="is-small" />
</b-tooltip>
</a>
<a href='' @click.prevent="deleteSubscriber(props.row)" data-cy="btn-delete">
<b-tooltip :label="$t('globals.buttons.delete')" type="is-dark">
<b-icon icon="trash-can-outline" size="is-small" />
</b-tooltip>
</a>
</div>
</b-table-column>
</template>
<template slot="empty" v-if="!loading.subscribers">
<template #empty v-if="!loading.subscribers">
<empty-placeholder />
</template>
</b-table>

View file

@ -14,70 +14,71 @@
<b-table :data="templates" :hoverable="true" :loading="loading.templates"
default-sort="createdAt">
<template slot-scope="props">
<b-table-column field="name" :label="$t('globals.fields.name')" sortable>
<a :href="props.row.id" @click.prevent="showEditForm(props.row)">
{{ props.row.name }}
</a>
<b-tag v-if="props.row.isDefault">{{ $t('templates.default') }}</b-tag>
</b-table-column>
<b-table-column v-slot="props" field="name" :label="$t('globals.fields.name')"
:td-attrs="$utils.tdID" sortable>
<a :href="props.row.id" @click.prevent="showEditForm(props.row)">
{{ props.row.name }}
</a>
<b-tag v-if="props.row.isDefault">{{ $t('templates.default') }}</b-tag>
</b-table-column>
<b-table-column field="createdAt" :label="$t('globals.fields.createdAt')" sortable>
{{ $utils.niceDate(props.row.createdAt) }}
</b-table-column>
<b-table-column v-slot="props" field="createdAt"
:label="$t('globals.fields.createdAt')" sortable>
{{ $utils.niceDate(props.row.createdAt) }}
</b-table-column>
<b-table-column field="updatedAt" :label="$t('globals.fields.updatedAt')" sortable>
{{ $utils.niceDate(props.row.updatedAt) }}
</b-table-column>
<b-table-column v-slot="props" field="updatedAt"
:label="$t('globals.fields.updatedAt')" sortable>
{{ $utils.niceDate(props.row.updatedAt) }}
</b-table-column>
<b-table-column class="actions" align="right">
<div>
<a href="#" @click.prevent="previewTemplate(props.row)" data-cy="btn-preview">
<b-tooltip :label="$t('templates.preview')" type="is-dark">
<b-icon icon="file-find-outline" size="is-small" />
</b-tooltip>
</a>
<a href="#" @click.prevent="showEditForm(props.row)" data-cy="btn-edit">
<b-tooltip :label="$t('globals.buttons.edit')" type="is-dark">
<b-icon icon="pencil-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" @click.prevent="$utils.prompt(`Clone template`,
{ placeholder: 'Name', value: `Copy of ${props.row.name}`},
(name) => cloneTemplate(name, props.row))"
data-cy="btn-clone">
<b-tooltip :label="$t('globals.buttons.clone')" type="is-dark">
<b-icon icon="file-multiple-outline" size="is-small" />
</b-tooltip>
</a>
<a v-if="!props.row.isDefault" href="#"
@click.prevent="$utils.confirm(null, () => makeTemplateDefault(props.row))"
data-cy="btn-set-default">
<b-tooltip :label="$t('templates.makeDefault')" type="is-dark">
<b-icon icon="check-circle-outline" size="is-small" />
</b-tooltip>
</a>
<span v-else class="a has-text-grey-light">
<b-icon icon="check-circle-outline" size="is-small" />
</span>
<b-table-column v-slot="props" cell-class="actions" align="right">
<div>
<a href="#" @click.prevent="previewTemplate(props.row)" data-cy="btn-preview">
<b-tooltip :label="$t('templates.preview')" type="is-dark">
<b-icon icon="file-find-outline" size="is-small" />
</b-tooltip>
</a>
<a href="#" @click.prevent="showEditForm(props.row)" data-cy="btn-edit">
<b-tooltip :label="$t('globals.buttons.edit')" type="is-dark">
<b-icon icon="pencil-outline" size="is-small" />
</b-tooltip>
</a>
<a href="" @click.prevent="$utils.prompt(`Clone template`,
{ placeholder: 'Name', value: `Copy of ${props.row.name}`},
(name) => cloneTemplate(name, props.row))"
data-cy="btn-clone">
<b-tooltip :label="$t('globals.buttons.clone')" type="is-dark">
<b-icon icon="file-multiple-outline" size="is-small" />
</b-tooltip>
</a>
<a v-if="!props.row.isDefault" href="#"
@click.prevent="$utils.confirm(null, () => makeTemplateDefault(props.row))"
data-cy="btn-set-default">
<b-tooltip :label="$t('templates.makeDefault')" type="is-dark">
<b-icon icon="check-circle-outline" size="is-small" />
</b-tooltip>
</a>
<span v-else class="a has-text-grey-light">
<b-icon icon="check-circle-outline" size="is-small" />
</span>
<a v-if="!props.row.isDefault" href="#"
@click.prevent="$utils.confirm(null, () => deleteTemplate(props.row))"
data-cy="btn-delete">
<b-tooltip :label="$t('globals.buttons.delete')" type="is-dark">
<b-icon icon="trash-can-outline" size="is-small" />
</b-tooltip>
</a>
<span v-else class="a has-text-grey-light">
<b-icon icon="trash-can-outline" size="is-small" />
</span>
</div>
</b-table-column>
</template>
<a v-if="!props.row.isDefault" href="#"
@click.prevent="$utils.confirm(null, () => deleteTemplate(props.row))"
data-cy="btn-delete">
<b-tooltip :label="$t('globals.buttons.delete')" type="is-dark">
<b-icon icon="trash-can-outline" size="is-small" />
</b-tooltip>
</a>
<span v-else class="a has-text-grey-light">
<b-icon icon="trash-can-outline" size="is-small" />
</span>
</div>
</b-table-column>
<template slot="empty" v-if="!loading.templates">
<empty-placeholder />
</template>
<template #empty v-if="!loading.templates">
<empty-placeholder />
</template>
</b-table>
<!-- Add / edit form modal -->

18
frontend/yarn.lock vendored
View file

@ -2606,12 +2606,12 @@ browserslist@^4.14.5, browserslist@^4.16.6:
escalade "^3.1.1"
node-releases "^1.1.71"
buefy@^0.8.20:
version "0.8.20"
resolved "https://registry.yarnpkg.com/buefy/-/buefy-0.8.20.tgz#75708800548220654575903d031a81fc8575b7f3"
integrity sha512-pg8Cn0m9cjqp2/vaKT4VIfU8KIumuX/gAT1GtearXRs56+kKqAPx3j9O8cm9W6P4jPUCHajKX6H8AqD0ram2Bg==
buefy@^0.9.7:
version "0.9.7"
resolved "https://registry.yarnpkg.com/buefy/-/buefy-0.9.7.tgz#694e73fe0b32632a53d94c5ba9cfa4468363badd"
integrity sha512-Fli0ZjNDgtFtHm0LItWmfhNJ1oLjDwPzUWccvwXXoo2mADXaH8JQxyhY+drUuUV5/GMu5PtwqQSqPgZy942VZg==
dependencies:
bulma "0.7.5"
bulma "0.9.2"
buffer-crc32@~0.2.3:
version "0.2.13"
@ -2652,10 +2652,10 @@ builtin-status-codes@^3.0.0:
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
bulma@0.7.5:
version "0.7.5"
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.7.5.tgz#35066c37f82c088b68f94450be758fc00a967208"
integrity sha512-cX98TIn0I6sKba/DhW0FBjtaDpxTelU166pf7ICXpCCuplHWyu6C9LYZmL5PEsnePIeJaiorsTEzzNk3Tsm1hw==
bulma@0.9.2:
version "0.9.2"
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.9.2.tgz#340011e119c605f19b8ca886bfea595f1deaf23c"
integrity sha512-e14EF+3VSZ488yL/lJH0tR8mFWiEQVCMi/BQUMi2TGMBOk+zrDg4wryuwm/+dRSHJw0gMawp2tsW7X1JYUCE3A==
bytes@3.0.0:
version "3.0.0"