Changeset View
Changeset View
Standalone View
Standalone View
src/resources/vue/Admin/User.vue
Show First 20 Lines • Show All 191 Lines • ▼ Show 20 Lines | <div class="container"> | ||||
<div class="card-body"> | <div class="card-body"> | ||||
<div class="card-text"> | <div class="card-text"> | ||||
<list-table :list="skus" :setup="skusListSetup" class="mb-0"></list-table> | <list-table :list="skus" :setup="skusListSetup" class="mb-0"></list-table> | ||||
<small v-if="discount > 0" class="hint"> | <small v-if="discount > 0" class="hint"> | ||||
<hr class="m-0"> | <hr class="m-0"> | ||||
¹ {{ $t('user.discount-hint') }}: {{ discount }}% - {{ discount_description }} | ¹ {{ $t('user.discount-hint') }}: {{ discount }}% - {{ discount_description }} | ||||
</small> | </small> | ||||
<div class="mt-2 buttons"> | <div class="mt-2 buttons"> | ||||
<btn class="btn-danger" id="reset2fa" v-if="has2FA" @click="reset2FADialog">{{ $t('user.reset-2fa') }}</btn> | <btn class="btn-danger" id="reset2fa" v-if="has2FA" @click="$refs.reset2faDialog.show()">{{ $t('user.reset-2fa') }}</btn> | ||||
<btn class="btn-secondary" id="addbetasku" v-if="!hasBeta" @click="addBetaSku">{{ $t('user.add-beta') }}</btn> | <btn class="btn-secondary" id="addbetasku" v-if="!hasBeta" @click="addBetaSku">{{ $t('user.add-beta') }}</btn> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="tab-pane" id="user-domains" role="tabpanel" aria-labelledby="tab-domains"> | <div class="tab-pane" id="user-domains" role="tabpanel" aria-labelledby="tab-domains"> | ||||
<div class="card-body"> | <div class="card-body"> | ||||
<div class="card-text"> | <div class="card-text"> | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | <div class="container"> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</form> | </form> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div id="discount-dialog" class="modal" tabindex="-1" role="dialog"> | <modal-dialog id="discount-dialog" ref="discountDialog" :title="$t('user.discount-title')" @click="submitDiscount()" :buttons="['submit']"> | ||||
<div class="modal-dialog" role="document"> | <div> | ||||
<div class="modal-content"> | |||||
<div class="modal-header"> | |||||
<h5 class="modal-title">{{ $t('user.discount-title') }}</h5> | |||||
<btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn> | |||||
</div> | |||||
<div class="modal-body"> | |||||
<p> | |||||
<select v-model="wallet.discount_id" class="form-select"> | <select v-model="wallet.discount_id" class="form-select"> | ||||
<option value="">- {{ $t('form.none') }} -</option> | <option value="">- {{ $t('form.none') }} -</option> | ||||
<option v-for="item in discounts" :value="item.id" :key="item.id">{{ item.label }}</option> | <option v-for="item in discounts" :value="item.id" :key="item.id">{{ item.label }}</option> | ||||
</select> | </select> | ||||
</p> | |||||
</div> | |||||
<div class="modal-footer"> | |||||
<btn class="btn-secondary modal-cancel" data-bs-dismiss="modal">{{ $t('btn.cancel') }}</btn> | |||||
<btn class="btn-primary modal-action" @click="submitDiscount()" icon="check">{{ $t('btn.submit') }}</btn> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | </div> | ||||
</modal-dialog> | |||||
<div id="email-dialog" class="modal" tabindex="-1" role="dialog"> | <modal-dialog id="email-dialog" ref="emailDialog" :title="$t('user.ext-email')" @click="submitEmail()" :buttons="['submit']"> | ||||
<div class="modal-dialog" role="document"> | <div> | ||||
<div class="modal-content"> | |||||
<div class="modal-header"> | |||||
<h5 class="modal-title">{{ $t('user.ext-email') }}</h5> | |||||
<btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn> | |||||
</div> | |||||
<div class="modal-body"> | |||||
<p> | |||||
<input v-model="external_email" name="external_email" class="form-control"> | <input v-model="external_email" name="external_email" class="form-control"> | ||||
</p> | |||||
</div> | |||||
<div class="modal-footer"> | |||||
<btn class="btn-secondary modal-cancel" data-bs-dismiss="modal">{{ $t('btn.cancel') }}</btn> | |||||
<btn class="btn-primary modal-action" @click="submitEmail()" icon="check">{{ $t('btn.submit') }}</btn> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | </div> | ||||
</modal-dialog> | |||||
<div id="oneoff-dialog" class="modal" tabindex="-1" role="dialog"> | <modal-dialog id="oneoff-dialog" ref="oneoffDialog" @click="submitOneOff()" :buttons="['submit']" | ||||
<div class="modal-dialog" role="document"> | :title="$t(oneoff_negative ? 'user.add-penalty-title' : 'user.add-bonus-title')" | ||||
<div class="modal-content"> | > | ||||
<div class="modal-header"> | |||||
<h5 class="modal-title">{{ $t(oneoff_negative ? 'user.add-penalty-title' : 'user.add-bonus-title') }}</h5> | |||||
<btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn> | |||||
</div> | |||||
<div class="modal-body"> | |||||
<form data-validation-prefix="oneoff_"> | <form data-validation-prefix="oneoff_"> | ||||
<div class="row mb-3"> | <div class="mb-3"> | ||||
<label for="oneoff_amount" class="col-form-label">{{ $t('form.amount') }}</label> | <label for="oneoff_amount" class="form-label">{{ $t('form.amount') }}</label> | ||||
<div class="input-group"> | <div class="input-group"> | ||||
<input type="text" class="form-control" id="oneoff_amount" v-model="oneoff_amount" required> | <input type="text" class="form-control" id="oneoff_amount" v-model="oneoff_amount" required> | ||||
<span class="input-group-text">{{ wallet.currency }}</span> | <span class="input-group-text">{{ wallet.currency }}</span> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="row"> | <div> | ||||
<label for="oneoff_description" class="col-form-label">{{ $t('form.description') }}</label> | <label for="oneoff_description" class="form-label">{{ $t('form.description') }}</label> | ||||
<input class="form-control" id="oneoff_description" v-model="oneoff_description" required> | <input class="form-control" id="oneoff_description" v-model="oneoff_description" required> | ||||
</div> | </div> | ||||
</form> | </form> | ||||
</div> | </modal-dialog> | ||||
<div class="modal-footer"> | |||||
<btn class="btn-secondary modal-cancel" data-bs-dismiss="modal">{{ $t('btn.cancel') }}</btn> | |||||
<btn class="btn-primary modal-action" @click="submitOneOff()" icon="check">{{ $t('btn.submit') }}</btn> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div id="reset-2fa-dialog" class="modal" tabindex="-1" role="dialog"> | <modal-dialog id="reset-2fa-dialog" ref="reset2faDialog" :title="$t('user.reset-2fa-title')" @click="reset2FA()" | ||||
<div class="modal-dialog" role="document"> | :buttons="[{className: 'btn-danger modal-action', label: 'btn.reset'}]" | ||||
<div class="modal-content"> | > | ||||
<div class="modal-header"> | |||||
<h5 class="modal-title">{{ $t('user.reset-2fa-title') }}</h5> | |||||
<btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn> | |||||
</div> | |||||
<div class="modal-body"> | |||||
<p>{{ $t('user.2fa-hint1') }}</p> | <p>{{ $t('user.2fa-hint1') }}</p> | ||||
<p>{{ $t('user.2fa-hint2') }}</p> | <p>{{ $t('user.2fa-hint2') }}</p> | ||||
</div> | </modal-dialog> | ||||
<div class="modal-footer"> | |||||
<btn class="btn-secondary modal-cancel" data-bs-dismiss="modal">{{ $t('btn.cancel') }}</btn> | |||||
<btn class="btn-danger modal-action" @click="reset2FA()">{{ $t('btn.reset') }}</btn> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | </div> | ||||
</template> | </template> | ||||
<script> | <script> | ||||
import { Modal } from 'bootstrap' | import ModalDialog from '../Widgets/ModalDialog' | ||||
import TransactionLog from '../Widgets/TransactionLog' | import TransactionLog from '../Widgets/TransactionLog' | ||||
import { ListTable } from '../Widgets/ListTools' | import { ListTable } from '../Widgets/ListTools' | ||||
import { default as DistlistList } from '../Distlist/ListWidget' | import { default as DistlistList } from '../Distlist/ListWidget' | ||||
import { default as DomainList } from '../Domain/ListWidget' | import { default as DomainList } from '../Domain/ListWidget' | ||||
import { default as ResourceList } from '../Resource/ListWidget' | import { default as ResourceList } from '../Resource/ListWidget' | ||||
import { default as SharedFolderList } from '../SharedFolder/ListWidget' | import { default as SharedFolderList } from '../SharedFolder/ListWidget' | ||||
import { default as UserList } from '../User/ListWidget' | import { default as UserList } from '../User/ListWidget' | ||||
import { library } from '@fortawesome/fontawesome-svg-core' | import { library } from '@fortawesome/fontawesome-svg-core' | ||||
library.add( | library.add( | ||||
require('@fortawesome/free-solid-svg-icons/faFolderOpen').definition, | require('@fortawesome/free-solid-svg-icons/faFolderOpen').definition, | ||||
require('@fortawesome/free-solid-svg-icons/faGear').definition, | require('@fortawesome/free-solid-svg-icons/faGear').definition, | ||||
require('@fortawesome/free-solid-svg-icons/faGlobe').definition, | require('@fortawesome/free-solid-svg-icons/faGlobe').definition, | ||||
require('@fortawesome/free-solid-svg-icons/faUsers').definition, | require('@fortawesome/free-solid-svg-icons/faUsers').definition, | ||||
) | ) | ||||
export default { | export default { | ||||
components: { | components: { | ||||
DistlistList, | DistlistList, | ||||
DomainList, | DomainList, | ||||
ListTable, | ListTable, | ||||
ModalDialog, | |||||
ResourceList, | ResourceList, | ||||
SharedFolderList, | SharedFolderList, | ||||
TransactionLog, | TransactionLog, | ||||
UserList | UserList | ||||
}, | }, | ||||
beforeRouteUpdate (to, from, next) { | beforeRouteUpdate (to, from, next) { | ||||
// An event called when the route that renders this component has changed, | // An event called when the route that renders this component has changed, | ||||
// but this component is reused in the new route. | // but this component is reused in the new route. | ||||
▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | export default { | ||||
.then(response => { | .then(response => { | ||||
this.folders = response.data.list | this.folders = response.data.list | ||||
}) | }) | ||||
}) | }) | ||||
.catch(this.$root.errorHandler) | .catch(this.$root.errorHandler) | ||||
}, | }, | ||||
mounted() { | mounted() { | ||||
$(this.$el).find('ul.nav-tabs a').on('click', this.$root.tab) | $(this.$el).find('ul.nav-tabs a').on('click', this.$root.tab) | ||||
this.$refs.discountDialog.events({ | |||||
shown: () => { | |||||
// Note: Vue v-model is strict, convert null to a string | |||||
this.wallet.discount_id = this.wallet.discount_id || '' | |||||
} | |||||
}) | |||||
}, | }, | ||||
methods: { | methods: { | ||||
addBetaSku() { | addBetaSku() { | ||||
axios.post('/api/v4/users/' + this.user.id + '/skus/beta') | axios.post('/api/v4/users/' + this.user.id + '/skus/beta') | ||||
.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) | ||||
this.hasBeta = true | this.hasBeta = true | ||||
Show All 9 Lines | export default { | ||||
}, | }, | ||||
capitalize(str) { | capitalize(str) { | ||||
return str.charAt(0).toUpperCase() + str.slice(1) | return str.charAt(0).toUpperCase() + str.slice(1) | ||||
}, | }, | ||||
awardDialog() { | awardDialog() { | ||||
this.oneOffDialog(false) | this.oneOffDialog(false) | ||||
}, | }, | ||||
discountEdit() { | discountEdit() { | ||||
if (!this.discount_dialog) { | this.$refs.discountDialog.show() | ||||
const dialog = $('#discount-dialog')[0] | |||||
dialog.addEventListener('shown.bs.modal', e => { | |||||
$(dialog).find('select').focus() | |||||
// Note: Vue v-model is strict, convert null to a string | |||||
this.wallet.discount_id = this.wallet_discount_id || '' | |||||
}) | |||||
this.discount_dialog = new Modal(dialog) | |||||
} | |||||
this.discount_dialog.show() | |||||
if (!this.discounts.length) { | if (!this.discounts.length) { | ||||
// Fetch discounts | // Fetch discounts | ||||
axios.get('/api/v4/users/' + this.user.id + '/discounts') | axios.get('/api/v4/users/' + this.user.id + '/discounts') | ||||
.then(response => { | .then(response => { | ||||
this.discounts = response.data.list | this.discounts = response.data.list | ||||
}) | }) | ||||
} | } | ||||
}, | }, | ||||
emailEdit() { | emailEdit() { | ||||
this.external_email = this.user.external_email | this.external_email = this.user.external_email | ||||
this.$root.clearFormValidation($('#email-dialog')) | this.$root.clearFormValidation($('#email-dialog')) | ||||
this.$refs.emailDialog.show() | |||||
if (!this.email_dialog) { | |||||
const dialog = $('#email-dialog')[0] | |||||
dialog.addEventListener('shown.bs.modal', e => { | |||||
$(dialog).find('input').focus() | |||||
}) | |||||
this.email_dialog = new Modal(dialog) | |||||
} | |||||
this.email_dialog.show() | |||||
}, | }, | ||||
setMandateState() { | setMandateState() { | ||||
let mandate = this.wallet.mandate | let mandate = this.wallet.mandate | ||||
if (mandate && mandate.id) { | if (mandate && mandate.id) { | ||||
if (!mandate.isValid) { | if (!mandate.isValid) { | ||||
this.wallet.mandateState = mandate.isPending ? 'pending' : 'invalid' | this.wallet.mandateState = mandate.isPending ? 'pending' : 'invalid' | ||||
} else if (mandate.isDisabled) { | } else if (mandate.isDisabled) { | ||||
this.wallet.mandateState = 'disabled' | this.wallet.mandateState = 'disabled' | ||||
} | } | ||||
} | } | ||||
}, | }, | ||||
oneOffDialog(negative) { | oneOffDialog(negative) { | ||||
this.oneoff_negative = negative | this.oneoff_negative = negative | ||||
this.$refs.oneoffDialog.show() | |||||
if (!this.oneoff_dialog) { | |||||
const dialog = $('#oneoff-dialog')[0] | |||||
dialog.addEventListener('shown.bs.modal', () => { | |||||
this.$root.clearFormValidation(dialog) | |||||
$(dialog).find('#oneoff_amount').focus() | |||||
}) | |||||
this.oneoff_dialog = new Modal(dialog) | |||||
} | |||||
this.oneoff_dialog.show() | |||||
}, | }, | ||||
penalizeDialog() { | penalizeDialog() { | ||||
this.oneOffDialog(true) | this.oneOffDialog(true) | ||||
}, | }, | ||||
reload() { | reload() { | ||||
// this is to reload transaction log | // this is to reload transaction log | ||||
this.walletReload = true | this.walletReload = true | ||||
this.$nextTick(() => { this.walletReload = false }) | this.$nextTick(() => { this.walletReload = false }) | ||||
}, | }, | ||||
reset2FA() { | reset2FA() { | ||||
new Modal('#reset-2fa-dialog').hide() | this.$refs.reset2faDialog.hide() | ||||
axios.post('/api/v4/users/' + this.user.id + '/reset2FA') | axios.post('/api/v4/users/' + this.user.id + '/reset2FA') | ||||
.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) | ||||
this.skus = this.skus.filter(sku => sku.id != this.sku2FA) | this.skus = this.skus.filter(sku => sku.id != this.sku2FA) | ||||
this.has2FA = false | this.has2FA = false | ||||
} | } | ||||
}) | }) | ||||
}, | }, | ||||
reset2FADialog() { | |||||
new Modal('#reset-2fa-dialog').show() | |||||
}, | |||||
submitDiscount() { | submitDiscount() { | ||||
this.discount_dialog.hide() | this.$refs.discountDialog.hide() | ||||
axios.put('/api/v4/wallets/' + this.user.wallets[0].id, { discount: this.wallet.discount_id }) | axios.put('/api/v4/wallets/' + this.user.wallets[0].id, { discount: this.wallet.discount_id }) | ||||
.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) | ||||
this.wallet = Object.assign({}, this.wallet, response.data) | this.wallet = Object.assign({}, this.wallet, response.data) | ||||
// Update prices in Subscriptions tab | // Update prices in Subscriptions tab | ||||
if (this.user.wallet.id == response.data.id) { | if (this.user.wallet.id == response.data.id) { | ||||
this.discount = this.wallet.discount | this.discount = this.wallet.discount | ||||
this.discount_description = this.wallet.discount_description | this.discount_description = this.wallet.discount_description | ||||
this.skus.forEach(sku => { | this.skus.forEach(sku => { | ||||
sku.price = this.$root.priceLabel(sku.cost, this.discount) | sku.price = this.$root.priceLabel(sku.cost, this.discount) | ||||
}) | }) | ||||
} | } | ||||
} | } | ||||
}) | }) | ||||
}, | }, | ||||
submitEmail() { | submitEmail() { | ||||
axios.put('/api/v4/users/' + this.user.id, { external_email: this.external_email }) | axios.put('/api/v4/users/' + this.user.id, { external_email: this.external_email }) | ||||
.then(response => { | .then(response => { | ||||
if (response.data.status == 'success') { | if (response.data.status == 'success') { | ||||
this.email_dialog.hide() | this.$refs.emailDialog.hide() | ||||
this.$toast.success(response.data.message) | this.$toast.success(response.data.message) | ||||
this.user.external_email = this.external_email | this.user.external_email = this.external_email | ||||
this.external_email = null // required because of Vue | this.external_email = null // required because of Vue | ||||
} | } | ||||
}) | }) | ||||
}, | }, | ||||
submitOneOff() { | submitOneOff() { | ||||
let wallet_id = this.user.wallets[0].id | let wallet_id = this.user.wallets[0].id | ||||
let post = { | let post = { | ||||
amount: this.oneoff_amount, | amount: this.oneoff_amount, | ||||
description: this.oneoff_description | description: this.oneoff_description | ||||
} | } | ||||
if (this.oneoff_negative && /^\d+(\.?\d+)?$/.test(post.amount)) { | if (this.oneoff_negative && /^\d+(\.?\d+)?$/.test(post.amount)) { | ||||
post.amount *= -1 | post.amount *= -1 | ||||
} | } | ||||
this.$root.clearFormValidation('#oneoff-dialog') | this.$root.clearFormValidation('#oneoff-dialog') | ||||
axios.post('/api/v4/wallets/' + wallet_id + '/one-off', post) | axios.post('/api/v4/wallets/' + wallet_id + '/one-off', post) | ||||
.then(response => { | .then(response => { | ||||
if (response.data.status == 'success') { | if (response.data.status == 'success') { | ||||
this.oneoff_dialog.hide() | this.$refs.oneoffDialog.hide() | ||||
this.$toast.success(response.data.message) | this.$toast.success(response.data.message) | ||||
this.wallet = Object.assign({}, this.wallet, {balance: response.data.balance}) | this.wallet = Object.assign({}, this.wallet, {balance: response.data.balance}) | ||||
this.oneoff_amount = '' | this.oneoff_amount = '' | ||||
this.oneoff_description = '' | this.oneoff_description = '' | ||||
this.reload() | this.reload() | ||||
} | } | ||||
}) | }) | ||||
}, | }, | ||||
Show All 21 Lines |