Page MenuHomePhorge

D3551.1775247597.diff
No OneTemporary

Authored By
Unknown
Size
77 KB
Referenced Files
None
Subscribers
None

D3551.1775247597.diff

diff --git a/src/resources/js/app.js b/src/resources/js/app.js
--- a/src/resources/js/app.js
+++ b/src/resources/js/app.js
@@ -257,7 +257,7 @@
dialog = form.$el
}
- dialog.__vue__.showDialog()
+ dialog.__vue__.show()
},
statusClass(obj) {
if (obj.isDeleted) {
diff --git a/src/resources/themes/app.scss b/src/resources/themes/app.scss
--- a/src/resources/themes/app.scss
+++ b/src/resources/themes/app.scss
@@ -277,6 +277,17 @@
}
}
+.modal {
+ .modal-dialog,
+ .modal-content {
+ max-height: calc(100vh - 3.5rem);
+ }
+
+ .modal-body {
+ overflow: auto !important;
+ }
+}
+
#status-box {
background-color: lighten($green, 35);
diff --git a/src/resources/vue/Admin/User.vue b/src/resources/vue/Admin/User.vue
--- a/src/resources/vue/Admin/User.vue
+++ b/src/resources/vue/Admin/User.vue
@@ -197,7 +197,7 @@
¹ {{ $t('user.discount-hint') }}: {{ discount }}% - {{ discount_description }}
</small>
<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>
</div>
</div>
@@ -257,102 +257,50 @@
</div>
</div>
- <div id="discount-dialog" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <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">
- <option value="">- {{ $t('form.none') }} -</option>
- <option v-for="item in discounts" :value="item.id" :key="item.id">{{ item.label }}</option>
- </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>
+ <modal-dialog id="discount-dialog" ref="discountDialog" :title="$t('user.discount-title')" @click="submitDiscount()" :buttons="['submit']">
+ <div>
+ <select v-model="wallet.discount_id" class="form-select">
+ <option value="">- {{ $t('form.none') }} -</option>
+ <option v-for="item in discounts" :value="item.id" :key="item.id">{{ item.label }}</option>
+ </select>
</div>
- </div>
+ </modal-dialog>
- <div id="email-dialog" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <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">
- </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>
+ <modal-dialog id="email-dialog" ref="emailDialog" :title="$t('user.ext-email')" @click="submitEmail()" :buttons="['submit']">
+ <div>
+ <input v-model="external_email" name="external_email" class="form-control">
</div>
- </div>
-
- <div id="oneoff-dialog" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <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_">
- <div class="row mb-3">
- <label for="oneoff_amount" class="col-form-label">{{ $t('form.amount') }}</label>
- <div class="input-group">
- <input type="text" class="form-control" id="oneoff_amount" v-model="oneoff_amount" required>
- <span class="input-group-text">{{ wallet.currency }}</span>
- </div>
- </div>
- <div class="row">
- <label for="oneoff_description" class="col-form-label">{{ $t('form.description') }}</label>
- <input class="form-control" id="oneoff_description" v-model="oneoff_description" required>
- </div>
- </form>
- </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="submitOneOff()" icon="check">{{ $t('btn.submit') }}</btn>
+ </modal-dialog>
+
+ <modal-dialog id="oneoff-dialog" ref="oneoffDialog" @click="submitOneOff()" :buttons="['submit']"
+ :title="$t(oneoff_negative ? 'user.add-penalty-title' : 'user.add-bonus-title')"
+ >
+ <form data-validation-prefix="oneoff_">
+ <div class="mb-3">
+ <label for="oneoff_amount" class="form-label">{{ $t('form.amount') }}</label>
+ <div class="input-group">
+ <input type="text" class="form-control" id="oneoff_amount" v-model="oneoff_amount" required>
+ <span class="input-group-text">{{ wallet.currency }}</span>
</div>
</div>
- </div>
- </div>
-
- <div id="reset-2fa-dialog" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <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-hint2') }}</p>
- </div>
- <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>
+ <label for="oneoff_description" class="form-label">{{ $t('form.description') }}</label>
+ <input class="form-control" id="oneoff_description" v-model="oneoff_description" required>
</div>
- </div>
- </div>
+ </form>
+ </modal-dialog>
+
+ <modal-dialog id="reset-2fa-dialog" ref="reset2faDialog" :title="$t('user.reset-2fa-title')" @click="reset2FA()"
+ :buttons="[{className: 'btn-danger modal-action', label: 'btn.reset'}]"
+ >
+ <p>{{ $t('user.2fa-hint1') }}</p>
+ <p>{{ $t('user.2fa-hint2') }}</p>
+ </modal-dialog>
</div>
</template>
<script>
- import { Modal } from 'bootstrap'
+ import ModalDialog from '../Widgets/ModalDialog'
import TransactionLog from '../Widgets/TransactionLog'
import { ListTable } from '../Widgets/ListTools'
import { default as DistlistList } from '../Distlist/ListWidget'
@@ -375,6 +323,7 @@
DistlistList,
DomainList,
ListTable,
+ ModalDialog,
ResourceList,
SharedFolderList,
TransactionLog,
@@ -535,6 +484,13 @@
},
mounted() {
$(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: {
addBetaSku() {
@@ -560,19 +516,7 @@
this.oneOffDialog(false)
},
discountEdit() {
- if (!this.discount_dialog) {
- 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()
+ this.$refs.discountDialog.show()
if (!this.discounts.length) {
// Fetch discounts
@@ -585,18 +529,7 @@
emailEdit() {
this.external_email = this.user.external_email
this.$root.clearFormValidation($('#email-dialog'))
-
- 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()
+ this.$refs.emailDialog.show()
},
setMandateState() {
let mandate = this.wallet.mandate
@@ -610,19 +543,7 @@
},
oneOffDialog(negative) {
this.oneoff_negative = negative
-
- 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()
+ this.$refs.oneoffDialog.show()
},
penalizeDialog() {
this.oneOffDialog(true)
@@ -633,7 +554,7 @@
this.$nextTick(() => { this.walletReload = false })
},
reset2FA() {
- new Modal('#reset-2fa-dialog').hide()
+ this.$refs.reset2faDialog.hide()
axios.post('/api/v4/users/' + this.user.id + '/reset2FA')
.then(response => {
if (response.data.status == 'success') {
@@ -643,11 +564,8 @@
}
})
},
- reset2FADialog() {
- new Modal('#reset-2fa-dialog').show()
- },
submitDiscount() {
- this.discount_dialog.hide()
+ this.$refs.discountDialog.hide()
axios.put('/api/v4/wallets/' + this.user.wallets[0].id, { discount: this.wallet.discount_id })
.then(response => {
@@ -671,7 +589,7 @@
axios.put('/api/v4/users/' + this.user.id, { external_email: this.external_email })
.then(response => {
if (response.data.status == 'success') {
- this.email_dialog.hide()
+ this.$refs.emailDialog.hide()
this.$toast.success(response.data.message)
this.user.external_email = this.external_email
this.external_email = null // required because of Vue
@@ -694,7 +612,7 @@
axios.post('/api/v4/wallets/' + wallet_id + '/one-off', post)
.then(response => {
if (response.data.status == 'success') {
- this.oneoff_dialog.hide()
+ this.$refs.oneoffDialog.hide()
this.$toast.success(response.data.message)
this.wallet = Object.assign({}, this.wallet, {balance: response.data.balance})
this.oneoff_amount = ''
diff --git a/src/resources/vue/Domain/Info.vue b/src/resources/vue/Domain/Info.vue
--- a/src/resources/vue/Domain/Info.vue
+++ b/src/resources/vue/Domain/Info.vue
@@ -6,7 +6,7 @@
<div class="card-body">
<div class="card-title" v-if="domain_id === 'new'">{{ $t('domain.new') }}</div>
<div class="card-title" v-else>{{ $t('form.domain') }}
- <btn class="btn-outline-danger button-delete float-end" @click="showDeleteConfirmation()" icon="trash-can">{{ $t('domain.delete') }}</btn>
+ <btn class="btn-outline-danger button-delete float-end" @click="$refs.deleteDialog.show()" icon="trash-can">{{ $t('domain.delete') }}</btn>
</div>
<div class="card-text">
<ul class="nav nav-tabs mt-3" role="tablist">
@@ -93,29 +93,18 @@
</div>
</div>
</div>
- <div id="delete-warning" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h5 class="modal-title">{{ $t('domain.delete-domain', { domain: domain.namespace }) }}</h5>
- <btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn>
- </div>
- <div class="modal-body">
- <p>{{ $t('domain.delete-text') }}</p>
- </div>
- <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="deleteDomain()" icon="trash-can">{{ $t('btn.delete') }}</btn>
- </div>
- </div>
- </div>
- </div>
+
+ <modal-dialog id="delete-warning" ref="deleteDialog" @click="deleteDomain()" :buttons="['delete']" :cancel-focus="true"
+ :title="$t('domain.delete-domain', { domain: domain.namespace })"
+ >
+ <p>{{ $t('domain.delete-text') }}</p>
+ </modal-dialog>
</div>
</template>
<script>
- import { Modal } from 'bootstrap'
import ListInput from '../Widgets/ListInput'
+ import ModalDialog from '../Widgets/ModalDialog'
import PackageSelect from '../Widgets/PackageSelect'
import StatusComponent from '../Widgets/Status'
import SubscriptionSelect from '../Widgets/SubscriptionSelect'
@@ -129,6 +118,7 @@
export default {
components: {
ListInput,
+ ModalDialog,
PackageSelect,
StatusComponent,
SubscriptionSelect
@@ -161,9 +151,6 @@
},
mounted() {
$('#namespace').focus()
- $('#delete-warning')[0].addEventListener('shown.bs.modal', event => {
- $(event.target).find('button.modal-cancel').focus()
- })
},
methods: {
confirm() {
@@ -189,10 +176,6 @@
}
})
},
- showDeleteConfirmation() {
- // Display the warning
- new Modal('#delete-warning').show()
- },
statusUpdate(domain) {
this.domain = Object.assign({}, this.domain, domain)
},
diff --git a/src/resources/vue/Meet/Room.vue b/src/resources/vue/Meet/Room.vue
--- a/src/resources/vue/Meet/Room.vue
+++ b/src/resources/vue/Meet/Room.vue
@@ -39,7 +39,7 @@
<button class="btn link-fullscreen open hidden" @click="switchFullscreen" :title="$t('meet.menu-fullscreen-exit')">
<svg-icon icon="compress"></svg-icon>
</button>
- <button class="btn link-options" v-if="isRoomOwner()" @click="roomOptions" :title="$t('meet.options')">
+ <button class="btn link-options" v-if="isRoomOwner()" @click="$refs.optionsDialog.show()" :title="$t('meet.options')">
<svg-icon icon="gear"></svg-icon>
</button>
<button class="btn link-logout" @click="logout" :title="$t('meet.menu-leave')">
@@ -121,70 +121,45 @@
<logon-form id="meet-auth" class="hidden" :dashboard="false" @success="authSuccess"></logon-form>
- <div id="leave-dialog" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h5 class="modal-title">{{ $t('meet.leave-title') }}</h5>
- <btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn>
- </div>
- <div class="modal-body">
- <p>{{ $t('meet.leave-body') }}</p>
- </div>
- <div class="modal-footer">
- <btn class="btn-danger modal-action" data-bs-dismiss="modal">{{ $t('btn.close') }}</btn>
- </div>
+ <modal-dialog id="leave-dialog" ref="leaveDialog" :title="$t('meet.leave-title')">
+ <p>{{ $t('meet.leave-body') }}</p>
+ </modal-dialog>
+
+ <modal-dialog id="media-setup-dialog" ref="setupDialog" :title="$t('meet.media-title')">
+ <form class="media-setup-form">
+ <div class="media-setup-preview"></div>
+ <div class="input-group mt-2">
+ <label for="setup-mic" class="input-group-text mb-0" :title="$t('meet.mic')">
+ <svg-icon icon="microphone"></svg-icon>
+ </label>
+ <select class="form-select" id="setup-mic" v-model="microphone" @change="setupMicrophoneChange">
+ <option value="">{{ $t('form.none') }}</option>
+ <option v-for="mic in setup.microphones" :value="mic.deviceId" :key="mic.deviceId">{{ mic.label }}</option>
+ </select>
</div>
- </div>
- </div>
-
- <div id="media-setup-dialog" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h5 class="modal-title">{{ $t('meet.media-title') }}</h5>
- <btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn>
- </div>
- <div class="modal-body">
- <form class="media-setup-form">
- <div class="media-setup-preview"></div>
- <div class="input-group mt-2">
- <label for="setup-mic" class="input-group-text mb-0" :title="$t('meet.mic')">
- <svg-icon icon="microphone"></svg-icon>
- </label>
- <select class="form-select" id="setup-mic" v-model="microphone" @change="setupMicrophoneChange">
- <option value="">{{ $t('form.none') }}</option>
- <option v-for="mic in setup.microphones" :value="mic.deviceId" :key="mic.deviceId">{{ mic.label }}</option>
- </select>
- </div>
- <div class="input-group mt-2">
- <label for="setup-cam" class="input-group-text mb-0" :title="$t('meet.cam')">
- <svg-icon icon="video"></svg-icon>
- </label>
- <select class="form-select" id="setup-cam" v-model="camera" @change="setupCameraChange">
- <option value="">{{ $t('form.none') }}</option>
- <option v-for="cam in setup.cameras" :value="cam.deviceId" :key="cam.deviceId">{{ cam.label }}</option>
- </select>
- </div>
- </form>
- </div>
- <div class="modal-footer">
- <btn class="btn-secondary modal-action" data-bs-dismiss="modal">{{ $t('btn.close') }}</btn>
- </div>
+ <div class="input-group mt-2">
+ <label for="setup-cam" class="input-group-text mb-0" :title="$t('meet.cam')">
+ <svg-icon icon="video"></svg-icon>
+ </label>
+ <select class="form-select" id="setup-cam" v-model="camera" @change="setupCameraChange">
+ <option value="">{{ $t('form.none') }}</option>
+ <option v-for="cam in setup.cameras" :value="cam.deviceId" :key="cam.deviceId">{{ cam.label }}</option>
+ </select>
</div>
- </div>
- </div>
+ </form>
+ </modal-dialog>
- <room-options v-if="session.config" :config="session.config" :room="room" @config-update="configUpdate"></room-options>
- <room-stats ref="roomStatsDialog" :stats="stats" :room="room"></room-stats>
+ <room-options v-if="session.config" :config="session.config" :room="room" @config-update="configUpdate" ref="optionsDialog"></room-options>
+ <room-stats ref="statsDialog" :room="room"></room-stats>
</div>
</template>
<script>
- import { Modal, Dropdown } from 'bootstrap'
+ import { Dropdown } from 'bootstrap'
import { Media } from '../../js/meet/media.js'
import { Room as Meet } from '../../js/meet/room.js'
import { Roles } from '../../js/meet/constants.js'
+ import ModalDialog from '../Widgets/ModalDialog'
import StatusMessage from '../Widgets/StatusMessage'
import LogonForm from '../Login'
import RoomOptions from './RoomOptions'
@@ -214,12 +189,12 @@
)
let roomRequest
- let statsRequest
const authHeader = 'X-Meet-Auth-Token'
export default {
components: {
LogonForm,
+ ModalDialog,
RoomOptions,
RoomStats,
StatusMessage
@@ -257,7 +232,6 @@
500: 'meet.status-500'
},
session: {},
- stats: {},
audioActive: false,
videoActive: false,
chatActive: false,
@@ -279,22 +253,21 @@
this.setupSession()
// Configure dialog events
- $('#leave-dialog')[0].addEventListener('hide.bs.modal', () => {
- // FIXME: Where exactly the user should land? Currently he'll land
- // on dashboard (if he's logged in) or login form (if he's not).
-
- this.$router.push({ name: 'dashboard' })
+ this.$refs.leaveDialog.events({
+ hide: () => {
+ // FIXME: Where exactly the user should land? Currently he'll land
+ // on dashboard (if he's logged in) or login form (if he's not).
+ this.$router.push({ name: 'dashboard' })
+ }
})
- const dialog = $('#media-setup-dialog')[0]
- dialog.addEventListener('show.bs.modal', () => { this.setupSession() })
- dialog.addEventListener('hide.bs.modal', () => { this.meet.setupStop() })
-
- this.roomStatsDialog = new Modal('#room-stats-dialog')
+ this.$refs.setupDialog.events({
+ show: () => { this.setupSession() },
+ hide: () => { this.meet.setupStop() }
+ })
},
beforeDestroy() {
clearTimeout(roomRequest)
- clearInterval(statsRequest)
$('#app').removeClass('meet')
@@ -317,10 +290,6 @@
configUpdate(config) {
this.session.config = Object.assign({}, this.session.config, config)
},
- async refreshStats() {
- let stats = await this.meet.getStats()
- this.stats = stats
- },
initSession(init) {
const button = $('#join-button').prop('disabled', true)
@@ -453,7 +422,6 @@
}
clearTimeout(roomRequest)
- clearInterval(statsRequest)
this.session.nickname = this.nickname
this.session.languages = this.languages
@@ -474,7 +442,7 @@
this.session.onDestroy = event => {
// TODO: Display different message for every other reason
if (event.reason == 'session-closed' && !this.isRoomOwner()) {
- new Modal('#leave-dialog').show()
+ this.$refs.leaveDialog.show()
}
}
this.session.onUpdate = data => { this.updateSession(data) }
@@ -482,8 +450,6 @@
this.meet.joinRoom(this.session)
- this.refreshStats()
-
this.keyboardShortcuts()
},
keyboardShortcuts() {
@@ -500,7 +466,7 @@
}
// Show stats with '?' key
if (e.key == '?') {
- this.roomStats()
+ this.$refs.statsDialog.toggle(this.meet)
}
})
},
@@ -537,18 +503,6 @@
roomOptions() {
new Modal('#room-options-dialog').show()
},
- roomStats() {
- clearInterval(statsRequest)
- if (this.roomStatsDialog.visible) {
- this.roomStatsDialog.hide()
- } else {
- this.refreshStats()
- statsRequest = setInterval(() => {
- this.refreshStats()
- }, 3000)
- this.roomStatsDialog.show()
- }
- },
setupMedia() {
const dialog = $('#media-setup-dialog')[0]
@@ -556,7 +510,7 @@
$('#meet-setup').find('video,div.volume').appendTo($('.media-setup-preview', dialog))
}
- new Modal(dialog).show()
+ this.$refs.setupDialog.show()
},
async setupSession() {
this.meet.setupStart({
diff --git a/src/resources/vue/Meet/RoomOptions.vue b/src/resources/vue/Meet/RoomOptions.vue
--- a/src/resources/vue/Meet/RoomOptions.vue
+++ b/src/resources/vue/Meet/RoomOptions.vue
@@ -1,59 +1,49 @@
<template>
- <div v-if="config">
- <div id="room-options-dialog" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h5 class="modal-title">{{ $t('meet.options') }}</h5>
- <btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn>
- </div>
- <div class="modal-body">
- <form id="room-options-password">
- <div id="password-input" class="input-group input-group-activable mb-2">
- <span class="input-group-text label">{{ $t('meet.password') }}:</span>
- <span v-if="config.password" id="password-input-text" class="input-group-text">{{ config.password }}</span>
- <span v-else id="password-input-text" class="input-group-text text-muted">{{ $t('meet.password-none') }}</span>
- <input type="text" :value="config.password" name="password" class="form-control rounded-start activable">
- <btn @click="passwordSave" id="password-save-btn" class="btn-outline-primary activable rounded-end">{{ $t('btn.save') }}</btn>
- <btn v-if="config.password" id="password-clear-btn" @click="passwordClear" class="btn-outline-danger rounded">{{ $t('meet.password-clear') }}</btn>
- <btn v-else @click="passwordSet" id="password-set-btn" class="btn-outline-primary rounded">{{ $t('meet.password-set') }}</btn>
- </div>
- <small class="text-muted">
- {{ $t('meet.password-text') }}
- </small>
- </form>
- <hr>
- <form id="room-options-lock">
- <div id="room-lock" class="mb-2">
- <label for="room-lock-input">{{ $t('meet.lock') }}:</label>
- <input type="checkbox" id="room-lock-input" name="lock" value="1" :checked="config.locked" @click="lockSave">
- </div>
- <small class="text-muted">
- {{ $t('meet.lock-text') }}
- </small>
- </form>
- <hr>
- <form id="room-options-nomedia">
- <div id="room-nomedia" class="mb-2">
- <label for="room-nomedia-input">{{ $t('meet.nomedia') }}:</label>
- <input type="checkbox" id="room-nomedia-input" name="lock" value="1" :checked="config.nomedia" @click="nomediaSave">
- </div>
- <small class="text-muted">
- {{ $t('meet.nomedia-text') }}
- </small>
- </form>
- </div>
- <div class="modal-footer">
- <btn class="btn-secondary modal-action" data-bs-dismiss="modal">{{ $t('btn.close') }}</btn>
- </div>
- </div>
+ <modal-dialog v-if="config" id="room-options-dialog" ref="dialog" :title="$t('meet.options')">
+ <form id="room-options-password">
+ <div id="password-input" class="input-group input-group-activable mb-2">
+ <span class="input-group-text label">{{ $t('meet.password') }}:</span>
+ <span v-if="config.password" id="password-input-text" class="input-group-text">{{ config.password }}</span>
+ <span v-else id="password-input-text" class="input-group-text text-muted">{{ $t('meet.password-none') }}</span>
+ <input type="text" :value="config.password" name="password" class="form-control rounded-start activable">
+ <btn @click="passwordSave" id="password-save-btn" class="btn-outline-primary activable rounded-end">{{ $t('btn.save') }}</btn>
+ <btn v-if="config.password" id="password-clear-btn" @click="passwordClear" class="btn-outline-danger rounded">{{ $t('meet.password-clear') }}</btn>
+ <btn v-else @click="passwordSet" id="password-set-btn" class="btn-outline-primary rounded">{{ $t('meet.password-set') }}</btn>
</div>
- </div>
- </div>
+ <small class="text-muted">
+ {{ $t('meet.password-text') }}
+ </small>
+ </form>
+ <hr>
+ <form id="room-options-lock">
+ <div id="room-lock" class="mb-2">
+ <label for="room-lock-input">{{ $t('meet.lock') }}:</label>
+ <input type="checkbox" id="room-lock-input" name="lock" value="1" :checked="config.locked" @click="lockSave">
+ </div>
+ <small class="text-muted">
+ {{ $t('meet.lock-text') }}
+ </small>
+ </form>
+ <hr>
+ <form id="room-options-nomedia">
+ <div id="room-nomedia" class="mb-2">
+ <label for="room-nomedia-input">{{ $t('meet.nomedia') }}:</label>
+ <input type="checkbox" id="room-nomedia-input" name="lock" value="1" :checked="config.nomedia" @click="nomediaSave">
+ </div>
+ <small class="text-muted">
+ {{ $t('meet.nomedia-text') }}
+ </small>
+ </form>
+ </modal-dialog>
</template>
<script>
+ import ModalDialog from '../Widgets/ModalDialog'
+
export default {
+ components: {
+ ModalDialog
+ },
props: {
config: { type: Object, default: () => null },
room: { type: String, default: () => null }
@@ -106,6 +96,9 @@
}
})
.focus()
+ },
+ show() {
+ this.$refs.dialog.show()
}
}
}
diff --git a/src/resources/vue/Meet/RoomStats.vue b/src/resources/vue/Meet/RoomStats.vue
--- a/src/resources/vue/Meet/RoomStats.vue
+++ b/src/resources/vue/Meet/RoomStats.vue
@@ -1,61 +1,78 @@
<template>
- <div id="room-stats-dialog" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <div class="modal-content" style="max-height: 90vh">
- <div class="modal-header">
- <h5 class="modal-title">Statistics</h5>
- <button type="button" class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></button>
- </div>
- <div class="modal-body overflow-auto">
- <ul class="list-group list-group-flush">
- <li class="list-group-item">
- <span class="fw-bold">Room Id:</span>
- <pre class="text-muted m-0">{{ room }}</pre>
- </li>
- <li class="list-group-item">
- <span class="fw-bold">Mediaserver Room Id:</span>
- <pre class="text-muted m-0">{{ stats.roomId }}</pre>
- </li>
- <li class="list-group-item">
- <span class="fw-bold">Send Transport state:</span>
- <pre class="text-muted m-0">{{ stats.sendTransportState }}</pre>
- <pre class="text-muted m-0">{{ toText(stats.sendTransportStats) }}</pre>
- </li>
- <li class="list-group-item">
- <span class="fw-bold">Receive Transport state:</span>
- <pre class="text-muted m-0">{{ stats.receiveTransportState }}</pre>
- <pre class="text-muted m-0">{{ toText(stats.receiveTransportStats) }}</pre>
- </li>
- <li class="list-group-item">
- <span class="fw-bold">Consumers:</span>
- <pre class="text-muted m-0">{{ toText(stats.consumerStats) }}</pre>
- </li>
- <li class="list-group-item">
- <span class="fw-bold">Camera Producer:</span>
- <pre class="text-muted m-0">{{ toText(stats.camProducerStats) }}</pre>
- </li>
- <li class="list-group-item">
- <span class="fw-bold">Mic Producer:</span>
- <pre class="text-muted m-0">{{ toText(stats.micProducerStats) }}</pre>
- </li>
- <li class="list-group-item">
- <span class="fw-bold">Screen Producer:</span>
- <pre class="text-muted m-0">{{ toText(stats.screenProducerStats) }}</pre>
- </li>
- </ul>
- </div>
- </div>
- </div>
- </div>
+ <modal-dialog id="room-stats-dialog" ref="dialog" title="Statistics">
+ <p>
+ <span class="fw-bold">Room Id:</span>
+ <pre class="text-muted m-0">{{ room }}</pre>
+ </p>
+ <p>
+ <span class="fw-bold">Mediaserver Room Id:</span>
+ <pre class="text-muted m-0">{{ stats.roomId }}</pre>
+ </p>
+ <p>
+ <span class="fw-bold">Sender Transport:</span>
+ <pre class="text-muted m-0">{{ stats.sendTransportState }}</pre>
+ <pre class="text-muted m-0">{{ toText(stats.sendTransportStats) }}</pre>
+ </p>
+ <p>
+ <span class="fw-bold">Receiver Transport:</span>
+ <pre class="text-muted m-0">{{ stats.receiveTransportState }}</pre>
+ <pre class="text-muted m-0">{{ toText(stats.receiveTransportStats) }}</pre>
+ </p>
+ <p>
+ <span class="fw-bold">Consumers:</span>
+ <pre class="text-muted m-0">{{ toText(stats.consumerStats) }}</pre>
+ </p>
+ <p>
+ <span class="fw-bold">Camera Producer:</span>
+ <pre class="text-muted m-0">{{ toText(stats.camProducerStats) }}</pre>
+ </p>
+ <p>
+ <span class="fw-bold">Mic Producer:</span>
+ <pre class="text-muted m-0">{{ toText(stats.micProducerStats) }}</pre>
+ </p>
+ <p>
+ <span class="fw-bold">Screen Producer:</span>
+ <pre class="text-muted m-0">{{ toText(stats.screenProducerStats) }}</pre>
+ </p>
+ </modal-dialog>
</template>
<script>
+ import ModalDialog from '../Widgets/ModalDialog'
+
export default {
+ components: {
+ ModalDialog
+ },
props: {
- stats: { type: Object, default: () => null },
room: { type: String, default: () => null }
},
+ data() {
+ return {
+ stats: {}
+ }
+ },
+ mounted() {
+ this.$refs.dialog.events({
+ show: () => {
+ clearInterval(this.statsRequest)
+ this.refreshStats()
+ this.statsRequest = setInterval(() => { this.refreshStats() }, 3000)
+ },
+ hide: () => {
+ clearInterval(this.statsRequest)
+ }
+ })
+ },
methods: {
+ async refreshStats() {
+ let stats = await this.meet.getStats()
+ this.stats = stats
+ },
+ toggle(meet) {
+ this.meet = meet
+ this.$refs.dialog[this.statsRequest ? 'hide' : 'show']()
+ },
toText(o) {
return JSON.stringify(o, null, ' ')
}
diff --git a/src/resources/vue/Reseller/Invitations.vue b/src/resources/vue/Reseller/Invitations.vue
--- a/src/resources/vue/Reseller/Invitations.vue
+++ b/src/resources/vue/Reseller/Invitations.vue
@@ -8,7 +8,7 @@
<div class="card-text">
<div class="mb-2 d-flex">
<list-search :placeholder="$t('invitation.search')" :on-search="searchInvitations"></list-search>
- <btn class="btn-success create-invite ms-1" @click="inviteUserDialog" icon="envelope-open-text">{{ $t('invitation.create') }}</btn>
+ <btn class="btn-success create-invite ms-1" @click="$refs.createDialog.show()" icon="envelope-open-text">{{ $t('invitation.create') }}</btn>
</div>
<list-table id="invitations-list" :list="invitations" :setup="setup">
@@ -30,39 +30,27 @@
</div>
</div>
- <div id="invite-create" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h5 class="modal-title">{{ $t('invitation.create-title') }}</h5>
- <btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn>
- </div>
- <div class="modal-body">
- <form>
- <p>{{ $t('invitation.create-email') }}</p>
- <div>
- <input id="email" type="text" class="form-control" name="email">
- </div>
- <div class="form-separator"><hr><span>{{ $t('form.or') }}</span></div>
- <p>{{ $t('invitation.create-csv') }}</p>
- <div>
- <input id="file" type="file" class="form-control" name="csv">
- </div>
- </form>
- </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" icon="paper-plane" @click="inviteUser()">{{ $t('invitation.send') }}</btn>
- </div>
+ <modal-dialog id="invite-create" ref="createDialog" :title="$t('invitation.create-title')" @click="inviteUser()"
+ :buttons="[{ className: 'btn-primary modal-action', icon: 'paper-plane', label: 'invitation.send' }]"
+ >
+ <form>
+ <p>{{ $t('invitation.create-email') }}</p>
+ <div>
+ <input id="email" type="text" class="form-control" name="email">
</div>
- </div>
- </div>
+ <div class="form-separator"><hr><span>{{ $t('form.or') }}</span></div>
+ <p>{{ $t('invitation.create-csv') }}</p>
+ <div>
+ <input id="file" type="file" class="form-control" name="csv">
+ </div>
+ </form>
+ </modal-dialog>
</div>
</template>
<script>
- import { Modal } from 'bootstrap'
import ListTools from '../Widgets/ListTools'
+ import ModalDialog from '../Widgets/ModalDialog'
import { library } from '@fortawesome/fontawesome-svg-core'
@@ -73,6 +61,9 @@
)
export default {
+ components: {
+ ModalDialog
+ },
mixins: [ ListTools ],
data() {
return {
@@ -98,8 +89,12 @@
mounted() {
this.loadInvitations({ init: true })
- $('#invite-create')[0].addEventListener('shown.bs.modal', event => {
- $('input', event.target).first().focus()
+ this.$refs.createDialog.events({
+ show: (event) => {
+ const form = $(event.target).find('form')
+ form.get(0).reset()
+ this.fileChange({ target: form.find('#file')[0] }) // resets file input label
+ }
})
},
methods: {
@@ -146,7 +141,7 @@
axios.post('/api/v4/invitations', post, params)
.then(response => {
if (response.data.status == 'success') {
- this.dialog.hide()
+ this.$refs.createDialog.hide()
this.$toast.success(response.data.message)
if (response.data.count) {
this.loadInvitations({ reset: true })
@@ -154,17 +149,6 @@
}
})
},
- inviteUserDialog() {
- const dialog = $('#invite-create')[0]
- const form = $('form', dialog)
-
- form.get(0).reset()
- this.fileChange({ target: form.find('#file')[0] }) // resets file input label
- this.$root.clearFormValidation(form)
-
- this.dialog = new Modal(dialog)
- this.dialog.show()
- },
loadInvitations(params) {
this.listSearch('invitations', '/api/v4/invitations', params)
},
diff --git a/src/resources/vue/User/Info.vue b/src/resources/vue/User/Info.vue
--- a/src/resources/vue/User/Info.vue
+++ b/src/resources/vue/User/Info.vue
@@ -111,29 +111,17 @@
</div>
</div>
</div>
- <div id="delete-warning" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h5 class="modal-title">{{ $t('user.delete-email', { email: user.email }) }}</h5>
- <btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn>
- </div>
- <div class="modal-body">
- <p>{{ $t('user.delete-text') }}</p>
- </div>
- <div class="modal-footer">
- <btn class="btn-secondary modal-cancel" data-bs-dismiss="modal">{{ $t('btn.cancel') }}</btn>
- <btn class="btn-danger modal-action" icon="trash-can" @click="deleteUser()">{{ $t('btn.delete') }}</btn>
- </div>
- </div>
- </div>
- </div>
+ <modal-dialog id="delete-warning" ref="deleteWarning" :buttons="['delete']" :cancel-focus="true" @click="deleteUser()"
+ :title="$t('user.delete-email', { email: user.email })"
+ >
+ <p>{{ $t('user.delete-text') }}</p>
+ </modal-dialog>
</div>
</template>
<script>
- import { Modal } from 'bootstrap'
import ListInput from '../Widgets/ListInput'
+ import ModalDialog from '../Widgets/ModalDialog'
import PackageSelect from '../Widgets/PackageSelect'
import PasswordInput from '../Widgets/PasswordInput'
import StatusComponent from '../Widgets/Status'
@@ -148,6 +136,7 @@
export default {
components: {
ListInput,
+ ModalDialog,
PackageSelect,
PasswordInput,
StatusComponent,
@@ -195,9 +184,6 @@
},
mounted() {
$('#first_name').focus()
- $('#delete-warning')[0].addEventListener('shown.bs.modal', event => {
- $(event.target).find('button.modal-cancel').focus()
- })
},
methods: {
passwordLinkCopy() {
@@ -314,7 +300,7 @@
this.$router.push({ name: 'profile-delete' })
} else {
// Display the warning
- new Modal('#delete-warning').show()
+ this.$refs.deleteWarning.show()
}
}
}
diff --git a/src/resources/vue/Wallet.vue b/src/resources/vue/Wallet.vue
--- a/src/resources/vue/Wallet.vue
+++ b/src/resources/vue/Wallet.vue
@@ -89,103 +89,75 @@
</div>
</div>
- <div id="payment-dialog" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h5 class="modal-title">{{ paymentDialogTitle }}</h5>
- <btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn>
+ <modal-dialog id="payment-dialog" ref="paymentDialog" :title="paymentDialogTitle" @click="payment" :buttons="dialogButtons">
+ <div id="payment-method" v-if="paymentForm == 'method'">
+ <form data-validation-prefix="mandate_">
+ <div id="payment-method-selection">
+ <a v-for="method in paymentMethods" :key="method.id" @click="selectPaymentMethod(method)" href="#" :class="'card link-' + method.id">
+ <svg-icon v-if="method.icon" :icon="[method.icon.prefix, method.icon.name]" />
+ <img v-if="method.image" :src="method.image" />
+ <span class="name">{{ method.name }}</span>
+ </a>
</div>
- <div class="modal-body">
- <div id="payment-method" v-if="paymentForm == 'method'">
- <form data-validation-prefix="mandate_">
- <div id="payment-method-selection">
- <a v-for="method in paymentMethods" :key="method.id" @click="selectPaymentMethod(method)" href="#" :class="'card link-' + method.id">
- <svg-icon v-if="method.icon" :icon="[method.icon.prefix, method.icon.name]" />
- <img v-if="method.image" :src="method.image" />
- <span class="name">{{ method.name }}</span>
- </a>
- </div>
- </form>
- </div>
- <div id="manual-payment" v-if="paymentForm == 'manual'">
- <p v-if="wallet.currency != selectedPaymentMethod.currency">
- {{ $t('wallet.currency-conv', { wc: wallet.currency, pc: selectedPaymentMethod.currency }) }}
- </p>
- <p v-if="selectedPaymentMethod.id == 'banktransfer'">
- {{ $t('wallet.banktransfer-hint') }}
- </p>
- <p>
- {{ $t('wallet.payment-amount-hint') }}
- </p>
- <form id="payment-form" @submit.prevent="payment">
- <div class="input-group">
- <input type="text" class="form-control" id="amount" v-model="amount" required>
- <span class="input-group-text">{{ wallet.currency }}</span>
- </div>
- <div v-if="wallet.currency != selectedPaymentMethod.currency && !isNaN(amount)" class="alert alert-warning m-0 mt-3">
- {{ $t('wallet.payment-warning', { price: $root.price(amount * selectedPaymentMethod.exchangeRate * 100, selectedPaymentMethod.currency) }) }}
- </div>
- </form>
+ </form>
+ </div>
+ <div id="manual-payment" v-if="paymentForm == 'manual'">
+ <p v-if="wallet.currency != selectedPaymentMethod.currency">
+ {{ $t('wallet.currency-conv', { wc: wallet.currency, pc: selectedPaymentMethod.currency }) }}
+ </p>
+ <p v-if="selectedPaymentMethod.id == 'banktransfer'">
+ {{ $t('wallet.banktransfer-hint') }}
+ </p>
+ <p>
+ {{ $t('wallet.payment-amount-hint') }}
+ </p>
+ <form id="payment-form" @submit.prevent="payment">
+ <div class="input-group">
+ <input type="text" class="form-control" id="amount" v-model="amount" required>
+ <span class="input-group-text">{{ wallet.currency }}</span>
+ </div>
+ <div v-if="wallet.currency != selectedPaymentMethod.currency && !isNaN(amount)" class="alert alert-warning m-0 mt-3">
+ {{ $t('wallet.payment-warning', { price: $root.price(amount * selectedPaymentMethod.exchangeRate * 100, selectedPaymentMethod.currency) }) }}
+ </div>
+ </form>
+ </div>
+ <div id="auto-payment" v-if="paymentForm == 'auto'">
+ <form data-validation-prefix="mandate_">
+ <p>
+ {{ $t('wallet.auto-payment-hint') }}
+ </p>
+ <div class="row mb-3">
+ <label for="mandate_amount" class="col-sm-6 col-form-label">{{ $t('wallet.fill-up') }}</label>
+ <div class="col-sm-6">
+ <div class="input-group">
+ <input type="text" class="form-control" id="mandate_amount" v-model="mandate.amount" required>
+ <span class="input-group-text">{{ wallet.currency }}</span>
+ </div>
</div>
- <div id="auto-payment" v-if="paymentForm == 'auto'">
- <form data-validation-prefix="mandate_">
- <p>
- {{ $t('wallet.auto-payment-hint') }}
- </p>
- <div class="row mb-3">
- <label for="mandate_amount" class="col-sm-6 col-form-label">{{ $t('wallet.fill-up') }}</label>
- <div class="col-sm-6">
- <div class="input-group">
- <input type="text" class="form-control" id="mandate_amount" v-model="mandate.amount" required>
- <span class="input-group-text">{{ wallet.currency }}</span>
- </div>
- </div>
- </div>
- <div class="row mb-3">
- <label for="mandate_balance" class="col-sm-6 col-form-label">{{ $t('wallet.when-below') }}</label>
- <div class="col-sm-6">
- <div class="input-group">
- <input type="text" class="form-control" id="mandate_balance" v-model="mandate.balance" required>
- <span class="input-group-text">{{ wallet.currency }}</span>
- </div>
- </div>
- </div>
- <p v-if="!mandate.isValid">
- {{ $t('wallet.auto-payment-next') }}
- </p>
- <div v-if="mandate.isValid && mandate.isDisabled" class="disabled-mandate alert alert-danger m-0">
- {{ $t('wallet.auto-payment-disabled-next') }}
- </div>
- </form>
+ </div>
+ <div class="row mb-3">
+ <label for="mandate_balance" class="col-sm-6 col-form-label">{{ $t('wallet.when-below') }}</label>
+ <div class="col-sm-6">
+ <div class="input-group">
+ <input type="text" class="form-control" id="mandate_balance" v-model="mandate.balance" required>
+ <span class="input-group-text">{{ wallet.currency }}</span>
+ </div>
</div>
</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" icon="check" @click="autoPayment"
- v-if="paymentForm == 'auto' && (mandate.isValid || mandate.isPending)"
- >
- {{ $t('btn.submit') }}
- </btn>
- <btn class="btn btn-primary modal-action" icon="check" @click="autoPayment"
- v-if="paymentForm == 'auto' && !mandate.isValid && !mandate.isPending"
- >
- {{ $t('btn.continue') }}
- </btn>
- <btn class="btn-primary modal-action" icon="check" @click="payment"
- v-if="paymentForm == 'manual'"
- >
- {{ $t('btn.continue') }}
- </btn>
+ <p v-if="!mandate.isValid">
+ {{ $t('wallet.auto-payment-next') }}
+ </p>
+ <div v-if="mandate.isValid && mandate.isDisabled" class="disabled-mandate alert alert-danger m-0">
+ {{ $t('wallet.auto-payment-disabled-next') }}
</div>
- </div>
+ </form>
</div>
- </div>
+ </modal-dialog>
</div>
</template>
<script>
- import { Modal } from 'bootstrap'
+ import ModalDialog from './Widgets/ModalDialog'
import TransactionLog from './Widgets/TransactionLog'
import PaymentLog from './Widgets/PaymentLog'
import { downloadFile } from '../js/utils'
@@ -200,6 +172,7 @@
export default {
components: {
+ ModalDialog,
TransactionLog,
PaymentLog
},
@@ -221,6 +194,27 @@
selectedPaymentMethod: null
}
},
+ computed: {
+ dialogButtons() {
+ if (this.paymentForm == 'method') {
+ return []
+ }
+
+ const button = {
+ className: 'btn-primary modal-action',
+ icon: 'check',
+ label: 'btn.submit'
+ }
+
+ if (this.paymentForm == 'manual'
+ || (this.paymentForm == 'auto' && !this.mandate.isValid && !this.mandate.isPending)
+ ) {
+ button.label = 'btn.continue'
+ }
+
+ return [ button ]
+ }
+ },
mounted() {
$('#wallet button').focus()
@@ -280,6 +274,10 @@
setTimeout(() => { $('#payment-dialog').find('#amount,#mandate_amount').focus() }, 10)
},
payment() {
+ if (this.paymentForm == 'auto') {
+ return this.autoPayment()
+ }
+
if (this.formLock) {
return
}
@@ -341,7 +339,7 @@
} else {
// an update
if (response.data.status == 'success') {
- this.dialog.hide();
+ this.$refs.paymentDialog.hide();
this.mandate = response.data
this.$toast.success(response.data.message)
}
@@ -367,8 +365,7 @@
this.nextForm = nextForm
this.paymentDialogTitle = this.$t(nextForm == 'auto' ? 'wallet.auto-payment-setup' : 'wallet.top-up')
- this.dialog = new Modal('#payment-dialog')
- this.dialog.show()
+ this.$refs.paymentDialog.show()
this.$nextTick().then(() => {
const type = nextForm == 'manual' ? 'oneoff' : 'recurring'
@@ -385,8 +382,7 @@
this.paymentDialogTitle = title
this.formLock = false
- this.dialog = new Modal('#payment-dialog')
- this.dialog.show()
+ this.$refs.paymentDialog.show()
},
receiptDownload() {
const receipt = $('#receipt-id').val()
diff --git a/src/resources/vue/Widgets/CompanionappList.vue b/src/resources/vue/Widgets/CompanionappList.vue
--- a/src/resources/vue/Widgets/CompanionappList.vue
+++ b/src/resources/vue/Widgets/CompanionappList.vue
@@ -1,35 +1,26 @@
<template>
<div>
- <btn icon="trash-can" class="btn-outline-danger button-delete float-end" @click="showDeleteConfirmation()">
+ <btn icon="trash-can" class="btn-outline-danger button-delete float-end" @click="$refs.deleteDialog.show()">
{{ $t('companion.delete') }}
</btn>
+
<list-table class="m-0" :list="entries" :setup="setup"></list-table>
<list-more v-if="hasMore" :on-click="loadMore"></list-more>
- <div id="delete-warning" class="modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <h5 class="modal-title">{{ $t('companion.remove-devices') }}</h5>
- <btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn>
- </div>
- <div class="modal-body">
- <p>{{ $t('companion.remove-devices-text') }}</p>
- </div>
- <div class="modal-footer">
- <btn class="btn-secondary modal-cancel" data-bs-dismiss="modal">{{ $t('btn.cancel') }}</btn>
- <btn class="btn-danger modal-action" data-bs-dismiss="modal" @click="removeDevices()" icon="trash-can">{{ $t('btn.delete') }}</btn>
- </div>
- </div>
- </div>
- </div>
+
+ <modal-dialog id="delete-warning" ref="deleteDialog" :title="$t('companion.remove-devices')" @click="removeDevices()" :buttons="['delete']" :cancel-focus="true">
+ <p>{{ $t('companion.remove-devices-text') }}</p>
+ </modal-dialog>
</div>
</template>
<script>
- import { Modal } from 'bootstrap'
import ListTools from './ListTools'
+ import ModalDialog from './ModalDialog'
export default {
+ components: {
+ ModalDialog
+ },
mixins: [ ListTools ],
data() {
return {
@@ -51,9 +42,6 @@
},
mounted() {
this.loadMore({ reset: true })
- $('#delete-warning')[0].addEventListener('shown.bs.modal', event => {
- $(event.target).find('button.modal-cancel').focus()
- })
},
methods: {
loadMore(params) {
@@ -68,11 +56,7 @@
this.loadMore({ reset: true })
})
.catch(this.$root.errorHandler)
- },
- showDeleteConfirmation() {
- // Display the warning
- new Modal('#delete-warning').show()
- },
+ }
}
}
</script>
diff --git a/src/resources/vue/Widgets/ModalDialog.vue b/src/resources/vue/Widgets/ModalDialog.vue
new file mode 100644
--- /dev/null
+++ b/src/resources/vue/Widgets/ModalDialog.vue
@@ -0,0 +1,90 @@
+<template>
+ <div class="modal" tabindex="-1" role="dialog" aria-hidden="true">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content" style="max-height: 90vh">
+ <div class="modal-header">
+ <h5 class="modal-title">{{ title }}</h5>
+ <btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn>
+ </div>
+ <div class="modal-body overflow-auto">
+ <slot></slot>
+ </div>
+ <div class="modal-footer">
+ <btn class="btn-secondary modal-cancel" data-bs-dismiss="modal">{{ $t(buttons.length ? 'btn.cancel' : 'btn.close') }}</btn>
+ <btn v-for="(button, index) in buttons" :key="index"
+ :class="btnProperty(button, 'className')"
+ :icon="btnProperty(button, 'icon')"
+ :data-bs-dismiss="btnProperty(button, 'dismiss')"
+ @click="$emit('click', $event)"
+ >
+ {{ $t(btnProperty(button, 'label')) }}
+ </btn>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+ import { Modal } from 'bootstrap'
+ import { clearFormValidation } from '../../js/utils'
+
+ // Common buttons
+ const buttonTypes = {
+ 'delete': {
+ className: 'btn-danger modal-action',
+ dismiss: 'modal',
+ label: 'btn.delete',
+ icon: 'trash-can'
+ },
+ submit: {
+ className: 'btn-primary modal-action',
+ label: 'btn.submit',
+ icon: 'check'
+ },
+ }
+
+ export default {
+ props: {
+ buttons: { type: Array, default: () => [] },
+ cancelFocus: { type: Boolean, default: false },
+ title: { type: String, default: '' }
+ },
+ mounted() {
+ this.$el.addEventListener('shown.bs.modal', event => {
+ clearFormValidation(event.target)
+
+ if (this.cancelFocus) {
+ $(event.target).find('button.modal-cancel').focus()
+ } else {
+ $(event.target).find('input,select').first().focus()
+ }
+ })
+
+ this.dialog = new Modal(this.$el)
+ },
+ methods: {
+ btnProperty(button, property) {
+ const isString = typeof button == 'string'
+ if (!isString && property in button) {
+ return button[property]
+ }
+
+ if (isString && button in buttonTypes && property in buttonTypes[button]) {
+ return buttonTypes[button][property]
+ }
+ },
+ events(events) {
+ for (const name in events) {
+ this.$el.addEventListener(name + '.bs.modal', events[name])
+ }
+ },
+ hide() {
+ this.dialog.hide()
+ },
+ show() {
+ this.dialog.show()
+ }
+ }
+ }
+</script>
diff --git a/src/resources/vue/Widgets/SupportForm.vue b/src/resources/vue/Widgets/SupportForm.vue
--- a/src/resources/vue/Widgets/SupportForm.vue
+++ b/src/resources/vue/Widgets/SupportForm.vue
@@ -1,47 +1,38 @@
<template>
- <div class="modal" id="support-dialog" tabindex="-1" role="dialog" aria-hidden="true">
- <div class="modal-dialog" role="document">
- <form class="modal-content" @submit.prevent="submit">
- <div class="modal-header">
- <h5 class="modal-title">{{ $t('support.title') }}</h5>
- <btn class="btn-close" data-bs-dismiss="modal" :aria-label="$t('btn.close')"></btn>
- </div>
- <div class="modal-body">
- <div class="mb-3">
- <label for="support-user" class="form-label">{{ $t('support.id') }}</label>
- <input id="support-user" type="text" class="form-control" :placeholder="$t('support.id-pl')" v-model="user" />
- <small class="text-muted">{{ $t('support.id-hint') }}</small>
- </div>
- <div class="mb-3">
- <label for="support-name" class="form-label">{{ $t('support.name') }}</label>
- <input id="support-name" type="text" class="form-control" :placeholder="$t('support.name-pl')" v-model="name" />
- </div>
- <div class="mb-3">
- <label for="support-email" class="form-label">{{ $t('support.email') }}</label>
- <input id="support-email" type="email" class="form-control" :placeholder="$t('support.email-pl')" v-model="email" required />
- </div>
- <div class="mb-3">
- <label for="support-summary" class="form-label">{{ $t('support.summary') }}</label>
- <input id="support-summary" type="text" class="form-control" :placeholder="$t('support.summary-pl')" v-model="summary" required />
- </div>
- <div>
- <label for="support-body" class="form-label">{{ $t('support.expl') }}</label>
- <textarea id="support-body" class="form-control" rows="5" v-model="body" required></textarea>
- </div>
- </div>
- <div class="modal-footer">
- <btn class="btn-secondary modal-cancel" data-bs-dismiss="modal">{{ $t('btn.cancel') }}</btn>
- <btn type="submit" class="btn-primary modal-action" icon="check">{{ $t('btn.submit') }}</btn>
- </div>
- </form>
- </div>
- </div>
+ <modal-dialog id="support-dialog" ref="dialog" :title="$t('support.title')" @click="submit" :buttons="['submit']">
+ <form>
+ <div class="mb-3">
+ <label for="support-user" class="form-label">{{ $t('support.id') }}</label>
+ <input id="support-user" type="text" class="form-control" :placeholder="$t('support.id-pl')" v-model="user" />
+ <small class="text-muted">{{ $t('support.id-hint') }}</small>
+ </div>
+ <div class="mb-3">
+ <label for="support-name" class="form-label">{{ $t('support.name') }}</label>
+ <input id="support-name" type="text" class="form-control" :placeholder="$t('support.name-pl')" v-model="name" />
+ </div>
+ <div class="mb-3">
+ <label for="support-email" class="form-label">{{ $t('support.email') }}</label>
+ <input id="support-email" type="email" class="form-control" :placeholder="$t('support.email-pl')" v-model="email" required />
+ </div>
+ <div class="mb-3">
+ <label for="support-summary" class="form-label">{{ $t('support.summary') }}</label>
+ <input id="support-summary" type="text" class="form-control" :placeholder="$t('support.summary-pl')" v-model="summary" required />
+ </div>
+ <div>
+ <label for="support-body" class="form-label">{{ $t('support.expl') }}</label>
+ <textarea id="support-body" class="form-control" rows="5" v-model="body" required></textarea>
+ </div>
+ </form>
+ </modal-dialog>
</template>
<script>
- import { Modal } from 'bootstrap'
+ import ModalDialog from '../Widgets/ModalDialog'
export default {
+ components: {
+ ModalDialog
+ },
data() {
return {
body: '',
@@ -52,32 +43,25 @@
}
},
mounted() {
- const dialog = this.$el
-
- dialog.addEventListener('hide.bs.modal', () => {
- this.lockForm(false)
- if (this.cancelToken) {
- this.cancelToken()
+ this.$refs.dialog.events({
+ hide: () => {
+ this.lockForm(false)
+ if (this.cancelToken) {
+ this.cancelToken()
+ this.cancelToken = null
+ }
+ },
+ show: () => {
this.cancelToken = null
}
})
-
- dialog.addEventListener('show.bs.modal', () => {
- this.cancelToken = null
- })
-
- dialog.addEventListener('shown.bs.modal', () => {
- $(dialog).find('input').first().focus()
- })
-
- this.dialog = new Modal(dialog)
},
methods: {
lockForm(lock) {
$(this.$el).find('input,textarea,.modal-action').prop('disabled', lock)
},
- showDialog() {
- this.dialog.show()
+ show() {
+ this.$refs.dialog.show()
},
submit() {
this.lockForm(true)
@@ -103,7 +87,7 @@
this.summary = ''
this.body = ''
this.lockForm(false)
- this.dialog.hide()
+ this.$refs.dialog.hide()
this.$toast.success(response.data.message)
})
.catch(error => {
diff --git a/src/tests/Browser.php b/src/tests/Browser.php
--- a/src/tests/Browser.php
+++ b/src/tests/Browser.php
@@ -279,6 +279,7 @@
if (
$entry['level'] != 'SEVERE'
|| strpos($entry['message'], 'Failed to load resource: the server responded with a status of')
+ || preg_match('/^\S+\.js [0-9]+:[0-9]+\s*$/', $entry['message'])
) {
$console[$idx] = null;
}
diff --git a/src/tests/Browser/Meet/RoomOptionsTest.php b/src/tests/Browser/Meet/RoomOptionsTest.php
--- a/src/tests/Browser/Meet/RoomOptionsTest.php
+++ b/src/tests/Browser/Meet/RoomOptionsTest.php
@@ -49,7 +49,7 @@
->click('@menu button.link-options')
->with(new Dialog('#room-options-dialog'), function (Browser $browser) use ($room) {
$browser->assertSeeIn('@title', 'Room options')
- ->assertSeeIn('@button-action', 'Close')
+ ->assertSeeIn('@button-cancel', 'Close')
->assertElementsCount('.modal-footer button', 1)
->assertSeeIn('#password-input .label', 'Password:')
->assertSeeIn('#password-input-text.text-muted', 'none')
@@ -71,7 +71,7 @@
->assertSeeIn('#password-input-text:not(.text-muted)', 'pass')
->assertSeeIn('#password-clear-btn.btn-outline-danger', 'Clear password')
->assertElementsCount('#password-input button', 1)
- ->click('@button-action');
+ ->click('@button-cancel');
$this->assertSame('pass', $room->fresh()->getSetting('password'));
});
@@ -107,7 +107,7 @@
->assertSeeIn('#password-input-text.text-muted', 'none')
->assertSeeIn('#password-set-btn', 'Set password')
->assertElementsCount('#password-input button', 1)
- ->click('@button-action');
+ ->click('@button-cancel');
$this->assertSame(null, $room->fresh()->getSetting('password'));
});
@@ -143,7 +143,7 @@
// Test setting the lock
->click('#room-lock input')
->assertToast(Toast::TYPE_SUCCESS, "Room configuration updated successfully.")
- ->click('@button-action');
+ ->click('@button-cancel');
$this->assertSame('true', $room->fresh()->getSetting('locked'));
});
@@ -217,7 +217,7 @@
// Test setting the lock
->click('#room-lock input')
->assertToast(Toast::TYPE_SUCCESS, "Room configuration updated successfully.")
- ->click('@button-action');
+ ->click('@button-cancel');
$this->assertSame('true', $room->fresh()->getSetting('locked'));
});
@@ -260,8 +260,8 @@
$guest->with(new Dialog('#leave-dialog'), function (Browser $browser) {
$browser->assertSeeIn('@title', 'Room closed')
->assertSeeIn('@body', "The session has been closed by the room owner.")
- ->assertMissing('@button-cancel')
- ->assertSeeIn('@button-action', 'Close');
+ ->assertMissing('@button-action')
+ ->assertSeeIn('@button-cancel', 'Close');
});
});
}
@@ -295,7 +295,7 @@
// Test enabling the option
->click('#room-nomedia input')
->assertToast(Toast::TYPE_SUCCESS, "Room configuration updated successfully.")
- ->click('@button-action');
+ ->click('@button-cancel');
$this->assertSame('true', $room->fresh()->getSetting('nomedia'));
});
@@ -317,7 +317,7 @@
// Test enabling the option
->click('#room-nomedia input')
->assertToast(Toast::TYPE_SUCCESS, "Room configuration updated successfully.")
- ->click('@button-action');
+ ->click('@button-cancel');
$this->assertSame(null, $room->fresh()->getSetting('nomedia'));
});

File Metadata

Mime Type
text/plain
Expires
Fri, Apr 3, 8:19 PM (1 d, 9 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18826297
Default Alt Text
D3551.1775247597.diff (77 KB)

Event Timeline