mirror of
https://github.com/Bubka/2FAuth.git
synced 2025-05-29 22:39:11 +02:00
Set up the Edit twofaccount view
This commit is contained in:
parent
fb49e55e34
commit
beb5ee027a
6
resources/js_vue3/assets/app.scss
vendored
6
resources/js_vue3/assets/app.scss
vendored
@ -871,15 +871,15 @@ button.button.tag.is-white,
|
|||||||
background-color: $success-dark;
|
background-color: $success-dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-mid-width-field input {
|
.is-mid-width-field {
|
||||||
width: 50% !important;
|
width: 50% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-half-width-field input {
|
.is-half-width-field {
|
||||||
width: 50% !important;
|
width: 50% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-third-width-field input {
|
.is-third-width-field {
|
||||||
width: 33% !important;
|
width: 33% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
97
resources/js_vue3/components/formElements/FormLockField.vue
Normal file
97
resources/js_vue3/components/formElements/FormLockField.vue
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<script setup>
|
||||||
|
import { useIdGenerator } from '@/composables/helpers'
|
||||||
|
import { UseColorMode } from '@vueuse/components'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const { inputId } = useIdGenerator(props.inputType, props.fieldName)
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: [String, Number, Boolean],
|
||||||
|
isEditMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
fieldName: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
fieldError: [String],
|
||||||
|
inputType: {
|
||||||
|
type: String,
|
||||||
|
default: 'text'
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
help: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
hasOffset: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
isDisabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
isExpanded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
type: Number,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const fieldIsLocked = ref(props.isDisabled || props.isEditMode)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="field" style="margin-bottom: 0.5rem;">
|
||||||
|
<label :for="inputId" class="label" v-html="$t(label)" />
|
||||||
|
</div>
|
||||||
|
<div class="field has-addons" :class="{ 'pt-3' : hasOffset }">
|
||||||
|
<div class="control" :class="{ 'is-expanded': isExpanded }">
|
||||||
|
<input
|
||||||
|
:disabled="fieldIsLocked"
|
||||||
|
:id="inputId"
|
||||||
|
:type="inputType"
|
||||||
|
class="input"
|
||||||
|
:value="modelValue"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
v-bind="$attrs"
|
||||||
|
v-on:change="$emit('update:modelValue', $event.target.value)"
|
||||||
|
:maxlength="this.maxLength"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<UseColorMode v-slot="{ mode }" v-if="isEditMode">
|
||||||
|
<div class="control" v-if="fieldIsLocked">
|
||||||
|
<button type="button" class="button field-lock" :class="{'is-dark' : mode == 'dark'}" @click.stop="fieldIsLocked = false" :title="$t('twofaccounts.forms.unlock.title')">
|
||||||
|
<span class="icon">
|
||||||
|
<FontAwesomeIcon :icon="['fas', 'lock']" />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="control" v-else>
|
||||||
|
<button type="button" class="button field-unlock" :class="{'is-dark' : mode == 'dark'}" @click.stop="fieldIsLocked = true" :title="$t('twofaccounts.forms.lock.title')">
|
||||||
|
<span class="icon has-text-danger">
|
||||||
|
<FontAwesomeIcon :icon="['fas', 'lock-open']" />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</UseColorMode>
|
||||||
|
</div>
|
||||||
|
<FieldError v-if="fieldError != undefined" :error="fieldError" :field="fieldName" />
|
||||||
|
<p class="help" v-html="$t(help)" v-if="help"></p>
|
||||||
|
</template>
|
50
resources/js_vue3/router/index.js
vendored
50
resources/js_vue3/router/index.js
vendored
@ -3,29 +3,29 @@ import middlewarePipeline from "@/router/middlewarePipeline";
|
|||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { useTwofaccounts } from '@/stores/twofaccounts'
|
import { useTwofaccounts } from '@/stores/twofaccounts'
|
||||||
|
|
||||||
import Start from '../views/Start.vue'
|
import Start from '../views/Start.vue'
|
||||||
import Accounts from '../views/Accounts.vue'
|
import Accounts from '../views/Accounts.vue'
|
||||||
import Capture from '../views/twofaccounts/Capture.vue'
|
import Capture from '../views/twofaccounts/Capture.vue'
|
||||||
import CreateAccount from '../views/twofaccounts/Create.vue'
|
import CreateUpdateAccount from '../views/twofaccounts/CreateUpdate.vue'
|
||||||
import EditAccount from '../views/twofaccounts/Edit.vue'
|
import EditAccount from '../views/twofaccounts/Edit.vue'
|
||||||
import ImportAccount from '../views/twofaccounts/Import.vue'
|
import ImportAccount from '../views/twofaccounts/Import.vue'
|
||||||
import QRcodeAccount from '../views/twofaccounts/QRcode.vue'
|
import QRcodeAccount from '../views/twofaccounts/QRcode.vue'
|
||||||
import Groups from '../views/groups/Groups.vue'
|
import Groups from '../views/groups/Groups.vue'
|
||||||
import CreateUpdateGroup from '../views/groups/CreateUpdate.vue'
|
import CreateUpdateGroup from '../views/groups/CreateUpdate.vue'
|
||||||
import Login from '../views/auth/Login.vue'
|
import Login from '../views/auth/Login.vue'
|
||||||
import Register from '../views/auth/Register.vue'
|
import Register from '../views/auth/Register.vue'
|
||||||
// import Autolock from './views/auth/Autolock.vue'
|
// import Autolock from './views/auth/Autolock.vue'
|
||||||
import PasswordRequest from '../views/auth/password/Request.vue'
|
import PasswordRequest from '../views/auth/password/Request.vue'
|
||||||
// import PasswordReset from './views/auth/password/Reset.vue'
|
// import PasswordReset from './views/auth/password/Reset.vue'
|
||||||
import WebauthnLost from '../views/auth/webauthn/Lost.vue'
|
import WebauthnLost from '../views/auth/webauthn/Lost.vue'
|
||||||
// import WebauthnRecover from './views/auth/webauthn/Recover.vue'
|
// import WebauthnRecover from './views/auth/webauthn/Recover.vue'
|
||||||
import SettingsOptions from '../views/settings/Options.vue'
|
import SettingsOptions from '../views/settings/Options.vue'
|
||||||
import SettingsAccount from '../views/settings/Account.vue'
|
import SettingsAccount from '../views/settings/Account.vue'
|
||||||
import SettingsOAuth from '../views/settings/OAuth.vue'
|
import SettingsOAuth from '../views/settings/OAuth.vue'
|
||||||
import SettingsWebAuthn from '../views/settings/WebAuthn.vue'
|
import SettingsWebAuthn from '../views/settings/WebAuthn.vue'
|
||||||
import EditCredential from '../views/settings/Credentials/Edit.vue'
|
import EditCredential from '../views/settings/Credentials/Edit.vue'
|
||||||
import Errors from '../views/Error.vue'
|
import Errors from '../views/Error.vue'
|
||||||
import About from '../views/About.vue'
|
import About from '../views/About.vue'
|
||||||
|
|
||||||
import authGuard from './middlewares/authGuard'
|
import authGuard from './middlewares/authGuard'
|
||||||
import starter from './middlewares/starter'
|
import starter from './middlewares/starter'
|
||||||
@ -38,9 +38,9 @@ const router = createRouter({
|
|||||||
{ path: '/capture', name: 'capture', component: Capture, meta: { middlewares: [authGuard] } },
|
{ path: '/capture', name: 'capture', component: Capture, meta: { middlewares: [authGuard] } },
|
||||||
|
|
||||||
{ path: '/accounts', name: 'accounts', component: Accounts, meta: { middlewares: [authGuard, starter] }, alias: '/' },
|
{ path: '/accounts', name: 'accounts', component: Accounts, meta: { middlewares: [authGuard, starter] }, alias: '/' },
|
||||||
{ path: '/account/create', name: 'createAccount', component: CreateAccount, meta: { middlewares: [authGuard] } },
|
{ path: '/account/create', name: 'createAccount', component: CreateUpdateAccount, meta: { middlewares: [authGuard] } },
|
||||||
{ path: '/account/import', name: 'importAccounts', component: ImportAccount, meta: { middlewares: [authGuard] } },
|
{ path: '/account/import', name: 'importAccounts', component: ImportAccount, meta: { middlewares: [authGuard] } },
|
||||||
{ path: '/account/:twofaccountId/edit', name: 'editAccount', component: EditAccount, meta: { middlewares: [authGuard] } },
|
{ path: '/account/:twofaccountId/edit', name: 'editAccount', component: CreateUpdateAccount, meta: { middlewares: [authGuard] }, props: true },
|
||||||
{ path: '/account/:twofaccountId/qrcode', name: 'showQRcode', component: QRcodeAccount, meta: { middlewares: [authGuard] } },
|
{ path: '/account/:twofaccountId/qrcode', name: 'showQRcode', component: QRcodeAccount, meta: { middlewares: [authGuard] } },
|
||||||
|
|
||||||
{ path: '/groups', name: 'groups', component: Groups, meta: { middlewares: [authGuard] }, props: true },
|
{ path: '/groups', name: 'groups', component: Groups, meta: { middlewares: [authGuard] }, props: true },
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Form from '@/components/formElements/Form'
|
import Form from '@/components/formElements/Form'
|
||||||
import OtpDisplay from '@/components/OtpDisplay.vue'
|
import OtpDisplay from '@/components/OtpDisplay.vue'
|
||||||
|
import FormLockField from '@/components/formelements/FormLockField.vue'
|
||||||
import twofaccountService from '@/services/twofaccountService'
|
import twofaccountService from '@/services/twofaccountService'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { useBusStore } from '@/stores/bus'
|
import { useBusStore } from '@/stores/bus'
|
||||||
@ -11,6 +12,7 @@
|
|||||||
const { copy } = useClipboard({ legacy: true })
|
const { copy } = useClipboard({ legacy: true })
|
||||||
const $2fauth = inject('2fauth')
|
const $2fauth = inject('2fauth')
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
const user = useUserStore()
|
const user = useUserStore()
|
||||||
const bus = useBusStore()
|
const bus = useBusStore()
|
||||||
const notify = useNotifyStore()
|
const notify = useNotifyStore()
|
||||||
@ -52,6 +54,7 @@
|
|||||||
const showAdvancedForm = ref(false)
|
const showAdvancedForm = ref(false)
|
||||||
const ShowTwofaccountInModal = ref(false)
|
const ShowTwofaccountInModal = ref(false)
|
||||||
const fetchingLogo = ref(false)
|
const fetchingLogo = ref(false)
|
||||||
|
const secretIsLocked = ref(false)
|
||||||
|
|
||||||
// $refs
|
// $refs
|
||||||
const iconInput = ref(null)
|
const iconInput = ref(null)
|
||||||
@ -60,9 +63,25 @@
|
|||||||
const qrcodeInputLabel = ref(null)
|
const qrcodeInputLabel = ref(null)
|
||||||
const qrcodeInput = ref(null)
|
const qrcodeInput = ref(null)
|
||||||
const iconInputLabel = ref(null)
|
const iconInputLabel = ref(null)
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
twofaccountId: [Number, String]
|
||||||
|
})
|
||||||
|
|
||||||
|
const isEditMode = computed(() => {
|
||||||
|
return props.twofaccountId != undefined
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if( bus.decodedUri ) {
|
if (route.name == 'editAccount') {
|
||||||
|
twofaccountService.get(props.twofaccountId).then(response => {
|
||||||
|
form.fill(response.data)
|
||||||
|
// set account icon as temp icon
|
||||||
|
tempIcon.value = form.icon
|
||||||
|
showAdvancedForm.value = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if( bus.decodedUri ) {
|
||||||
// the Start view provided an uri via the bus store so we parse it and prefill the quick form
|
// the Start view provided an uri via the bus store so we parse it and prefill the quick form
|
||||||
uri.value = bus.decodedUri
|
uri.value = bus.decodedUri
|
||||||
bus.decodedUri = null
|
bus.decodedUri = null
|
||||||
@ -115,6 +134,13 @@
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper to call the appropriate function at form submit
|
||||||
|
*/
|
||||||
|
function handleSubmit() {
|
||||||
|
isEditMode.value ? updateAccount() : createAccount()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submits the form to the backend to store the new account
|
* Submits the form to the backend to store the new account
|
||||||
*/
|
*/
|
||||||
@ -130,6 +156,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits the form to the backend to save the edited account
|
||||||
|
*/
|
||||||
|
async function updateAccount() {
|
||||||
|
// Set new icon and delete old one
|
||||||
|
if( tempIcon.value !== form.icon ) {
|
||||||
|
let oldIcon = ''
|
||||||
|
|
||||||
|
oldIcon = form.icon
|
||||||
|
|
||||||
|
form.icon = tempIcon.value
|
||||||
|
tempIcon.value = oldIcon
|
||||||
|
deleteIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
await form.put('/api/v1/twofaccounts/' + props.twofaccountId)
|
||||||
|
|
||||||
|
if( form.errors.any() === false ) {
|
||||||
|
notify.success({ text: trans('twofaccounts.account_updated') })
|
||||||
|
router.push({ name: 'accounts' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows an OTP generated with the infos filled in the form
|
* Shows an OTP generated with the infos filled in the form
|
||||||
* in order to preview or validated the password/the form data
|
* in order to preview or validated the password/the form data
|
||||||
@ -196,6 +245,8 @@
|
|||||||
// This could desynchronized the HOTP verification server and our local counter if the user never verified the HOTP but this
|
// This could desynchronized the HOTP verification server and our local counter if the user never verified the HOTP but this
|
||||||
// is acceptable (and HOTP counter can be edited by the way)
|
// is acceptable (and HOTP counter can be edited by the way)
|
||||||
form.counter = payload.nextHotpCounter
|
form.counter = payload.nextHotpCounter
|
||||||
|
|
||||||
|
//form.uri = payload.nextUri
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -315,7 +366,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<!-- Quick form -->
|
<!-- Quick form -->
|
||||||
<form @submit.prevent="createAccount" @keydown="form.onKeydown($event)" v-if="showQuickForm">
|
<form @submit.prevent="createAccount" @keydown="form.onKeydown($event)" v-if="!isEditMode && showQuickForm">
|
||||||
<div class="container preview has-text-centered">
|
<div class="container preview has-text-centered">
|
||||||
<div class="columns is-mobile">
|
<div class="columns is-mobile">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
@ -357,10 +408,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<!-- Full form -->
|
<!-- Full form -->
|
||||||
<FormWrapper :title="$t('twofaccounts.forms.new_account')" v-if="showAdvancedForm">
|
<FormWrapper :title="$t(isEditMode ? 'twofaccounts.forms.edit_account' : 'twofaccounts.forms.new_account')" v-if="showAdvancedForm">
|
||||||
<form @submit.prevent="createAccount" @keydown="form.onKeydown($event)">
|
<form @submit.prevent="handleSubmit" @keydown="form.onKeydown($event)">
|
||||||
<!-- qcode fileupload -->
|
<!-- qcode fileupload -->
|
||||||
<div class="field is-grouped">
|
<div v-if="!isEditMode" class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div role="button" tabindex="0" class="file is-black is-small" @keyup.enter="qrcodeInputLabel.click()">
|
<div role="button" tabindex="0" class="file is-black is-small" @keyup.enter="qrcodeInputLabel.click()">
|
||||||
<label class="file-label" :title="$t('twofaccounts.forms.use_qrcode.title')" ref="qrcodeInputLabel">
|
<label class="file-label" :title="$t('twofaccounts.forms.use_qrcode.title')" ref="qrcodeInputLabel">
|
||||||
@ -375,7 +426,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<FieldError v-if="form.errors.hasAny('qrcode')" :error="form.errors.get('qrcode')" :field="'qrcode'" class="help-for-file" />
|
<FieldError v-if="!isEditMode && form.errors.hasAny('qrcode')" :error="form.errors.get('qrcode')" :field="'qrcode'" class="help-for-file" />
|
||||||
<!-- service -->
|
<!-- service -->
|
||||||
<FormField v-model="form.service" fieldName="service" :fieldError="form.errors.get('email')" :isDisabled="form.otp_type === 'steamtotp'" label="twofaccounts.service" :placeholder="$t('twofaccounts.forms.service.placeholder')" autofocus />
|
<FormField v-model="form.service" fieldName="service" :fieldError="form.errors.get('email')" :isDisabled="form.otp_type === 'steamtotp'" label="twofaccounts.service" :placeholder="$t('twofaccounts.forms.service.placeholder')" autofocus />
|
||||||
<!-- account -->
|
<!-- account -->
|
||||||
@ -420,19 +471,11 @@
|
|||||||
<p v-if="user.preferences.getOfficialIcons" class="help" v-html="$t('twofaccounts.forms.i_m_lucky_legend')"></p>
|
<p v-if="user.preferences.getOfficialIcons" class="help" v-html="$t('twofaccounts.forms.i_m_lucky_legend')"></p>
|
||||||
</div>
|
</div>
|
||||||
<!-- otp type -->
|
<!-- otp type -->
|
||||||
<FormToggle v-model="form.otp_type" :choices="otp_types" fieldName="otp_type" :fieldError="form.errors.get('otp_type')" label="twofaccounts.forms.otp_type.label" help="twofaccounts.forms.otp_type.help" :hasOffset="true" />
|
<FormToggle v-model="form.otp_type" :isDisabled="isEditMode" :choices="otp_types" fieldName="otp_type" :fieldError="form.errors.get('otp_type')" label="twofaccounts.forms.otp_type.label" help="twofaccounts.forms.otp_type.help" :hasOffset="true" />
|
||||||
<div v-if="form.otp_type != ''">
|
<div v-if="form.otp_type != ''">
|
||||||
<!-- secret -->
|
<!-- secret -->
|
||||||
<label :for="useIdGenerator('text','secret')" class="label" v-html="$t('twofaccounts.forms.secret.label')"></label>
|
<FormLockField :isEditMode="isEditMode" v-model="form.secret" fieldName="secret" :fieldError="form.errors.get('secret')" label="twofaccounts.forms.secret.label" help="twofaccounts.forms.secret.help" />
|
||||||
<div class="field">
|
<!-- Options -->
|
||||||
<p class="control is-expanded">
|
|
||||||
<input :id="useIdGenerator('text','secret')" class="input" type="text" v-model="form.secret">
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<FieldError v-if="form.errors.hasAny('secret')" :error="form.errors.get('secret')" :field="'secret'" />
|
|
||||||
<p class="help" v-html="$t('twofaccounts.forms.secret.help')"></p>
|
|
||||||
</div>
|
|
||||||
<div v-if="form.otp_type !== 'steamtotp'">
|
<div v-if="form.otp_type !== 'steamtotp'">
|
||||||
<h2 class="title is-4 mt-5 mb-2">{{ $t('commons.options') }}</h2>
|
<h2 class="title is-4 mt-5 mb-2">{{ $t('commons.options') }}</h2>
|
||||||
<p class="help mb-4">
|
<p class="help mb-4">
|
||||||
@ -445,12 +488,12 @@
|
|||||||
<!-- TOTP period -->
|
<!-- TOTP period -->
|
||||||
<FormField v-if="form.otp_type === 'totp'" pattern="[0-9]{1,4}" :class="'is-third-width-field'" v-model="form.period" fieldName="period" :fieldError="form.errors.get('period')" label="twofaccounts.forms.period.label" help="twofaccounts.forms.period.help" :placeholder="$t('twofaccounts.forms.period.placeholder')" />
|
<FormField v-if="form.otp_type === 'totp'" pattern="[0-9]{1,4}" :class="'is-third-width-field'" v-model="form.period" fieldName="period" :fieldError="form.errors.get('period')" label="twofaccounts.forms.period.label" help="twofaccounts.forms.period.help" :placeholder="$t('twofaccounts.forms.period.placeholder')" />
|
||||||
<!-- HOTP counter -->
|
<!-- HOTP counter -->
|
||||||
<FormField v-if="form.otp_type === 'hotp'" pattern="[0-9]{1,4}" :class="'is-third-width-field'" v-model="form.counter" fieldName="counter" :fieldError="form.errors.get('counter')" label="twofaccounts.forms.counter.label" help="twofaccounts.forms.counter.help" :placeholder="$t('twofaccounts.forms.counter.placeholder')" />
|
<FormLockField v-if="form.otp_type === 'hotp'" pattern="[0-9]{1,4}" :isEditMode="isEditMode" :isExpanded="false" v-model="form.counter" fieldName="counter" :fieldError="form.errors.get('counter')" label="twofaccounts.forms.counter.label" :placeholder="$t('twofaccounts.forms.counter.placeholder')" :help="isEditMode ? 'twofaccounts.forms.counter.help_lock' : 'twofaccounts.forms.counter.help'" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<VueFooter :showButtons="true">
|
<VueFooter :showButtons="true">
|
||||||
<p class="control">
|
<p class="control">
|
||||||
<VueButton id="btnCreate" :isLoading="form.isBusy" class="is-rounded" >{{ $t('commons.create') }}</VueButton>
|
<VueButton :id="isEditMode ? 'btnUpdate' : 'btnCreate'" :isLoading="form.isBusy" class="is-rounded" >{{ isEditMode ? $t('commons.save') : $t('commons.create') }}</VueButton>
|
||||||
</p>
|
</p>
|
||||||
<p class="control" v-if="form.otp_type && form.secret">
|
<p class="control" v-if="form.otp_type && form.secret">
|
||||||
<button id="btnPreview" type="button" class="button is-success is-rounded" @click="previewOTP">{{ $t('twofaccounts.forms.test') }}</button>
|
<button id="btnPreview" type="button" class="button is-success is-rounded" @click="previewOTP">{{ $t('twofaccounts.forms.test') }}</button>
|
Loading…
x
Reference in New Issue
Block a user