Changeset View
Changeset View
Standalone View
Standalone View
src/resources/vue/Reseller/Invitations.vue
<template> | <template> | ||||
<div class="container"> | <div class="container"> | ||||
<div class="card" id="invitations"> | <div class="card" id="invitations"> | ||||
<div class="card-body"> | <div class="card-body"> | ||||
<div class="card-title"> | <div class="card-title"> | ||||
{{ $t('invitation.title') }} | {{ $t('invitation.title') }} | ||||
</div> | </div> | ||||
<div class="card-text"> | <div class="card-text"> | ||||
<div class="mb-2 d-flex"> | <div class="mb-2 d-flex"> | ||||
<form @submit.prevent="searchInvitations" id="search-form" class="input-group" style="flex:1"> | <list-search :placeholder="$t('invitation.search')" :on-search="searchInvitations"></list-search> | ||||
<input class="form-control" type="text" :placeholder="$t('invitation.search')" v-model="search"> | |||||
<button type="submit" class="btn btn-primary"><svg-icon icon="search"></svg-icon> {{ $t('btn.search') }}</button> | |||||
</form> | |||||
<div> | <div> | ||||
<button class="btn btn-success create-invite ms-1" @click="inviteUserDialog"> | <button class="btn btn-success create-invite ms-1" @click="inviteUserDialog"> | ||||
<svg-icon icon="envelope-open-text"></svg-icon> {{ $t('invitation.create') }} | <svg-icon icon="envelope-open-text"></svg-icon> {{ $t('invitation.create') }} | ||||
</button> | </button> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<table id="invitations-list" class="table table-sm table-hover"> | <table id="invitations-list" class="table table-sm table-hover"> | ||||
Show All 20 Lines | <div class="container"> | ||||
</button> | </button> | ||||
<button class="btn button-resend p-0 ms-1" :disabled="inv.isNew || inv.isCompleted" @click="resendInvite(inv.id)"> | <button class="btn button-resend p-0 ms-1" :disabled="inv.isNew || inv.isCompleted" @click="resendInvite(inv.id)"> | ||||
<svg-icon icon="redo"></svg-icon> | <svg-icon icon="redo"></svg-icon> | ||||
<span class="btn-label">{{ $t('btn.resend') }}</span> | <span class="btn-label">{{ $t('btn.resend') }}</span> | ||||
</button> | </button> | ||||
</td> | </td> | ||||
</tr> | </tr> | ||||
</tbody> | </tbody> | ||||
<tfoot class="table-fake-body"> | <list-foot :text="$t('invitation.empty-list')" colspan="3"></list-foot> | ||||
<tr> | |||||
<td colspan="3">{{ $t('invitation.empty-list') }}</td> | |||||
</tr> | |||||
</tfoot> | |||||
</table> | </table> | ||||
<div class="text-center p-3" id="more-loader" v-if="hasMore"> | <list-more v-if="hasMore" :on-click="loadInvitations"></list-more> | ||||
<button class="btn btn-secondary" @click="loadInvitations(true)">{{ $t('nav.more') }}</button> | |||||
</div> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div id="invite-create" class="modal" tabindex="-1" role="dialog"> | <div id="invite-create" class="modal" tabindex="-1" role="dialog"> | ||||
<div class="modal-dialog" role="document"> | <div class="modal-dialog" role="document"> | ||||
<div class="modal-content"> | <div class="modal-content"> | ||||
<div class="modal-header"> | <div class="modal-header"> | ||||
Show All 24 Lines | <div class="container"> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</template> | </template> | ||||
<script> | <script> | ||||
import { Modal } from 'bootstrap' | import { Modal } from 'bootstrap' | ||||
import { library } from '@fortawesome/fontawesome-svg-core' | import { library } from '@fortawesome/fontawesome-svg-core' | ||||
import { faEnvelopeOpenText, faPaperPlane, faRedo } from '@fortawesome/free-solid-svg-icons' | import { faEnvelopeOpenText, faPaperPlane, faRedo } from '@fortawesome/free-solid-svg-icons' | ||||
import ListTools from '../Widgets/ListTools' | |||||
library.add(faEnvelopeOpenText, faPaperPlane, faRedo) | library.add(faEnvelopeOpenText, faPaperPlane, faRedo) | ||||
export default { | export default { | ||||
mixins: [ ListTools ], | |||||
data() { | data() { | ||||
return { | return { | ||||
invitations: [], | invitations: [] | ||||
hasMore: false, | |||||
page: 1, | |||||
search: '' | |||||
} | } | ||||
}, | }, | ||||
mounted() { | mounted() { | ||||
this.$root.startLoading() | this.loadInvitations({ init: true }) | ||||
this.loadInvitations(null, () => this.$root.stopLoading()) | |||||
$('#invite-create')[0].addEventListener('shown.bs.modal', event => { | $('#invite-create')[0].addEventListener('shown.bs.modal', event => { | ||||
$('input', event.target).first().focus() | $('input', event.target).first().focus() | ||||
}) | }) | ||||
}, | }, | ||||
methods: { | methods: { | ||||
deleteInvite(id) { | deleteInvite(id) { | ||||
axios.delete('/api/v4/invitations/' + id) | axios.delete('/api/v4/invitations/' + id) | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | export default { | ||||
form.get(0).reset() | form.get(0).reset() | ||||
this.fileChange({ target: form.find('#file')[0] }) // resets file input label | this.fileChange({ target: form.find('#file')[0] }) // resets file input label | ||||
this.$root.clearFormValidation(form) | this.$root.clearFormValidation(form) | ||||
this.dialog = new Modal(dialog) | this.dialog = new Modal(dialog) | ||||
this.dialog.show() | this.dialog.show() | ||||
}, | }, | ||||
loadInvitations(params, callback) { | loadInvitations(params) { | ||||
let loader | this.listSearch('invitations', '/api/v4/invitations', params) | ||||
let get = {} | |||||
if (params) { | |||||
if (params.reset) { | |||||
this.invitations = [] | |||||
this.page = 0 | |||||
} | |||||
get.page = params.page || (this.page + 1) | |||||
if (typeof params === 'object' && 'search' in params) { | |||||
get.search = params.search | |||||
this.currentSearch = params.search | |||||
} else { | |||||
get.search = this.currentSearch | |||||
} | |||||
loader = $(get.page > 1 ? '#more-loader' : '#invitations-list tfoot td') | |||||
} else { | |||||
this.currentSearch = null | |||||
} | |||||
this.$root.addLoader(loader) | |||||
axios.get('/api/v4/invitations', { params: get }) | |||||
.then(response => { | |||||
this.$root.removeLoader(loader) | |||||
// Note: In Vue we can't just use .concat() | |||||
for (let i in response.data.list) { | |||||
this.$set(this.invitations, this.invitations.length, response.data.list[i]) | |||||
} | |||||
this.hasMore = response.data.hasMore | |||||
this.page = response.data.page || 1 | |||||
if (callback) { | |||||
callback() | |||||
} | |||||
}) | |||||
.catch(error => { | |||||
this.$root.removeLoader(loader) | |||||
if (callback) { | |||||
callback() | |||||
} | |||||
}) | |||||
}, | }, | ||||
resendInvite(id) { | resendInvite(id) { | ||||
axios.post('/api/v4/invitations/' + id + '/resend') | axios.post('/api/v4/invitations/' + id + '/resend') | ||||
.then(response => { | .then(response => { | ||||
if (response.data.status == 'success') { | if (response.data.status == 'success') { | ||||
this.$toast.success(response.data.message) | this.$toast.success(response.data.message) | ||||
// Update the invitation record | // Update the invitation record | ||||
const index = this.invitations.findIndex(item => item.id == id) | const index = this.invitations.findIndex(item => item.id == id) | ||||
if (index > -1) { | if (index > -1) { | ||||
this.$set(this.invitations, index, response.data.invitation) | this.$set(this.invitations, index, response.data.invitation) | ||||
} | } | ||||
} | } | ||||
}) | }) | ||||
}, | }, | ||||
searchInvitations() { | searchInvitations(search) { | ||||
this.loadInvitations({ reset: true, search: this.search }) | this.loadInvitations({ reset: true, search }) | ||||
}, | }, | ||||
statusClass(invitation) { | statusClass(invitation) { | ||||
if (invitation.isCompleted) { | if (invitation.isCompleted) { | ||||
return 'text-success' | return 'text-success' | ||||
} | } | ||||
if (invitation.isFailed) { | if (invitation.isFailed) { | ||||
return 'text-danger' | return 'text-danger' | ||||
Show All 26 Lines |