2FAuth/public/build/assets/CreateUpdate-erSWwknc.js.map
2024-11-27 12:03:02 +01:00

1 line
38 KiB
Plaintext

{"version":3,"file":"CreateUpdate-erSWwknc.js","sources":["../../../resources/js/components/formElements/FormLockField.vue","../../../resources/js/views/twofaccounts/CreateUpdate.vue"],"sourcesContent":["<script setup>\n import { useIdGenerator, useValidationErrorIdGenerator } from '@/composables/helpers'\n import { UseColorMode } from '@vueuse/components'\n\n defineOptions({\n inheritAttrs: false\n })\n \n const props = defineProps({\n modelValue: String,\n modelModifiers: { default: () => ({}) },\n isEditMode: {\n type: Boolean,\n default: false\n },\n label: {\n type: String,\n default: ''\n },\n fieldName: {\n type: String,\n default: '',\n required: true\n },\n fieldError: [String],\n inputType: {\n type: String,\n default: 'text'\n },\n placeholder: {\n type: String,\n default: ''\n },\n help: {\n type: String,\n default: ''\n },\n hasOffset: {\n type: Boolean,\n default: false\n },\n isDisabled: {\n type: Boolean,\n default: false\n },\n isExpanded: {\n type: Boolean,\n default: true\n },\n maxLength: {\n type: Number,\n default: null\n },\n idSuffix: {\n type: String,\n default: ''\n }\n })\n\n const { inputId } = useIdGenerator(props.inputType, props.fieldName + props.idSuffix)\n const { valErrorId } = useValidationErrorIdGenerator(props.fieldName)\n const legendId = useIdGenerator('legend', props.fieldName).inputId\n\n const fieldIsLocked = ref(props.isDisabled || props.isEditMode)\n const hasBeenTrimmed = ref(false)\n const componentKey = ref(0);\n\n const emit = defineEmits(['update:modelValue'])\n\n /**\n * Removes spaces from the input string\n */\n function emitValue(e) {\n let value = e.target.value\n \n\n if (props.modelModifiers.trimAll) {\n value = value.replace(/\\s+/g, '')\n }\n\n emit('update:modelValue', value)\n }\n\n function alertOnSpace(e) {\n let value = e.target.value\n hasBeenTrimmed.value = value.includes(' ')\n\n emit('update:modelValue', value)\n }\n\n function forceRefresh(e) {\n hasBeenTrimmed.value = e.target.value.includes(' ')\n componentKey.value += 1\n }\n\n</script>\n\n<template>\n <label :for=\"inputId\" class=\"label\" v-html=\"$t(label)\" />\n <div class=\"field has-addons mb-0\" :class=\"{ 'pt-3' : hasOffset }\">\n <div class=\"control\" :class=\"{ 'is-expanded': isExpanded }\">\n <input \n :key=\"componentKey\"\n :disabled=\"fieldIsLocked\" \n :id=\"inputId\" \n :type=\"inputType\" \n class=\"input\" \n :value=\"modelValue\" \n :placeholder=\"placeholder\" \n v-bind=\"$attrs\"\n v-on:input=\"alertOnSpace\"\n v-on:change=\"emitValue\"\n v-on:blur=\"forceRefresh\"\n :maxlength=\"maxLength\" \n :aria-describedby=\"help ? legendId : undefined\"\n :aria-invalid=\"fieldError != undefined\"\n :aria-errormessage=\"fieldError != undefined ? valErrorId : undefined\" \n />\n </div>\n <UseColorMode v-slot=\"{ mode }\" v-if=\"isEditMode\">\n <div class=\"control\" v-if=\"fieldIsLocked\">\n <button type=\"button\" class=\"button field-lock\" :class=\"{'is-dark' : mode == 'dark'}\" @click.stop=\"fieldIsLocked = false\" :title=\"$t('twofaccounts.forms.unlock.title')\">\n <span class=\"icon\">\n <FontAwesomeIcon :icon=\"['fas', 'lock']\" />\n </span>\n </button>\n </div>\n <div class=\"control\" v-else>\n <button type=\"button\" class=\"button field-unlock\" :class=\"{'is-dark' : mode == 'dark'}\" @click.stop=\"fieldIsLocked = true\" :title=\"$t('twofaccounts.forms.lock.title')\">\n <span class=\"icon has-text-danger\">\n <FontAwesomeIcon :icon=\"['fas', 'lock-open']\" />\n </span>\n </button>\n </div>\n </UseColorMode>\n </div>\n <FieldError v-if=\"hasBeenTrimmed\" :error=\"$t('twofaccounts.forms.spaces_are_ignored')\" :field=\"'spaces'\" :alertType=\"'is-warning'\" />\n <FieldError v-if=\"fieldError != undefined\" :error=\"fieldError\" :field=\"fieldName\" />\n <p :id=\"legendId\" class=\"help\" v-html=\"$t(help)\" v-if=\"help\"></p>\n</template>\n","<script setup>\n import Form from '@/components/formElements/Form'\n import OtpDisplay from '@/components/OtpDisplay.vue'\n import QrContentDisplay from '@/components/QrContentDisplay.vue'\n import FormLockField from '@/components/formElements/FormLockField.vue'\n import twofaccountService from '@/services/twofaccountService'\n import { useUserStore } from '@/stores/user'\n import { useTwofaccounts } from '@/stores/twofaccounts'\n import { useGroups } from '@/stores/groups'\n import { useBusStore } from '@/stores/bus'\n import { useNotifyStore } from '@/stores/notify'\n import { UseColorMode } from '@vueuse/components'\n \n const $2fauth = inject('2fauth')\n const router = useRouter()\n const route = useRoute()\n const user = useUserStore()\n const twofaccounts = useTwofaccounts()\n const bus = useBusStore()\n const notify = useNotifyStore()\n const form = reactive(new Form({\n service: '',\n account: '',\n otp_type: '',\n icon: '',\n group_id: user.preferences.defaultGroup == -1 ? user.preferences.activeGroup : user.preferences.defaultGroup,\n secret: '',\n algorithm: '',\n digits: null,\n counter: null,\n period: null,\n image: '',\n }))\n const qrcodeForm = reactive(new Form({\n qrcode: null\n }))\n const iconForm = reactive(new Form({\n icon: null\n }))\n const otpDisplayProps = ref({\n otp_type: '',\n account : '',\n service : '',\n icon : '',\n })\n const otp_types = [\n { text: 'TOTP', value: 'totp' },\n { text: 'HOTP', value: 'hotp' },\n { text: 'STEAM', value: 'steamtotp' },\n ]\n const digitsChoices = [\n { text: '6', value: 6 },\n { text: '7', value: 7 },\n { text: '8', value: 8 },\n { text: '9', value: 9 },\n { text: '10', value: 10 },\n ]\n const algorithms = [\n { text: 'sha1', value: 'sha1' },\n { text: 'sha256', value: 'sha256' },\n { text: 'sha512', value: 'sha512' },\n { text: 'md5', value: 'md5' },\n ]\n const uri = ref()\n const tempIcon = ref('')\n const showQuickForm = ref(false)\n const showAlternatives = ref(false)\n const showOtpInModal = ref(false)\n const showAdvancedForm = ref(false)\n const ShowTwofaccountInModal = ref(false)\n const fetchingLogo = ref(false)\n\n // $refs\n const iconInput = ref(null)\n const OtpDisplayForAutoSave = ref(null)\n const OtpDisplayForQuickForm = ref(null)\n const OtpDisplayForAdvancedForm = ref(null)\n const qrcodeInputLabel = ref(null)\n const qrcodeInput = ref(null)\n const iconInputLabel = ref(null)\n \n const props = defineProps({\n twofaccountId: [Number, String]\n })\n\n const isEditMode = computed(() => {\n return props.twofaccountId != undefined\n })\n\n const groups = computed(() => {\n return useGroups().items.map((item) => {\n return { text: item.id > 0 ? item.name : '- ' + trans('groups.no_group') + ' -', value: item.id }\n })\n })\n\n onMounted(() => {\n if (route.name == 'editAccount') {\n twofaccountService.get(props.twofaccountId).then(response => {\n form.fill(response.data)\n if (form.group_id == null) {\n form.group_id = 0\n }\n form.setOriginal()\n // set account icon as temp icon\n tempIcon.value = form.icon\n showAdvancedForm.value = true\n })\n }\n else if( bus.decodedUri ) {\n // The Start|Capture view provided an uri via the bus store.\n uri.value = bus.decodedUri\n bus.decodedUri = null\n\n if (user.preferences.AutoSaveQrcodedAccount) {\n // The user wants the account to be saved automatically.\n // We save it now and we show him a fresh otp\n twofaccountService.storeFromUri(uri.value).then(response => {\n showOTP(response.data)\n })\n .catch(error => {\n if( error.response.data.errors.uri ) {\n showAlternatives.value = true\n showAdvancedForm.value = true\n }\n })\n }\n else {\n // We prefill and show the quick form\n twofaccountService.preview(uri.value).then(response => {\n form.fill(response.data)\n tempIcon.value = response.data.icon ? response.data.icon : ''\n showQuickForm.value = true\n nextTick().then(() => {\n OtpDisplayForQuickForm.value.show()\n })\n })\n .catch(error => {\n if( error.response.data.errors.uri ) {\n showAlternatives.value = true\n showAdvancedForm.value = true\n }\n })\n }\n } else {\n showAdvancedForm.value = true\n }\n })\n\n watch(tempIcon, (val) => {\n if( showQuickForm.value ) {\n nextTick().then(() => {\n OtpDisplayForQuickForm.value.icon = val\n })\n }\n })\n\n watch(ShowTwofaccountInModal, (val) => {\n if (val == false) {\n OtpDisplayForAdvancedForm.value?.clearOTP()\n OtpDisplayForQuickForm.value?.clearOTP()\n }\n })\n\n watch(showOtpInModal, (val) => {\n if (val == false) {\n OtpDisplayForAutoSave.value?.clearOTP()\n router.push({ name: 'accounts' })\n }\n })\n\n watch(\n () => form.otp_type,\n (to, from) => {\n if (to === 'steamtotp') {\n form.service = 'Steam'\n fetchLogo()\n }\n else if (from === 'steamtotp') {\n form.service = ''\n deleteTempIcon()\n }\n }\n )\n\n /**\n * Wrapper to call the appropriate function at form submit\n */\n function handleSubmit() {\n isEditMode.value ? updateAccount() : createAccount()\n }\n\n /**\n * Submits the form to the backend to store the new account\n */\n async function createAccount() {\n // set current temp icon as account icon\n form.icon = tempIcon.value\n\n const { data } = await form.post('/api/v1/twofaccounts')\n\n if (form.errors.any() === false) {\n twofaccounts.items.push(data)\n notify.success({ text: trans('twofaccounts.account_created') })\n router.push({ name: 'accounts' });\n }\n }\n\n /**\n * Submits the form to the backend to save the edited account\n */\n async function updateAccount() {\n // Set new icon and delete old one\n if( tempIcon.value !== form.icon ) {\n let oldIcon = ''\n oldIcon = form.icon\n form.icon = tempIcon.value\n tempIcon.value = oldIcon\n deleteTempIcon()\n }\n\n const { data } = await form.put('/api/v1/twofaccounts/' + props.twofaccountId)\n\n if( form.errors.any() === false ) {\n const index = twofaccounts.items.findIndex(acc => acc.id === data.id)\n twofaccounts.items.splice(index, 1, data)\n\n notify.success({ text: trans('twofaccounts.account_updated') })\n router.push({ name: 'accounts' })\n }\n }\n\n /**\n * Shows an OTP generated with the infos filled in the form\n * in order to preview or validated the password/the form data\n */\n function previewOTP() {\n form.clear()\n ShowTwofaccountInModal.value = true\n OtpDisplayForAdvancedForm.value.show()\n }\n\n /**\n * Shows rotating OTP for the provided account\n */\n function showOTP(otp) {\n // Data that should be displayed quickly by the OtpDisplay\n // component are passed using props.\n otpDisplayProps.value.otp_type = otp.otp_type\n otpDisplayProps.value.service = otp.service\n otpDisplayProps.value.account = otp.account\n otpDisplayProps.value.icon = otp.icon\n\n nextTick().then(() => {\n showOtpInModal.value = true\n OtpDisplayForAutoSave.value.show(otp.id);\n })\n }\n\n /**\n * Exits the view with user confirmation\n */\n function cancelCreation() {\n if (form.hasChanged() || tempIcon.value != form.icon) {\n if (confirm(trans('twofaccounts.confirm.cancel')) === true) {\n if (!isEditMode.value || tempIcon.value != form.icon) {\n deleteTempIcon()\n }\n router.push({name: 'accounts'})\n }\n }\n else router.push({name: 'accounts'})\n }\n\n /**\n * Uploads the submited image resource to the backend\n */\n function uploadIcon() {\n // clean possible already uploaded temp icon\n deleteTempIcon()\n iconForm.icon = iconInput.value.files[0]\n\n iconForm.upload('/api/v1/icons', { returnError: true })\n .then(response => {\n tempIcon.value = response.data.filename\n if (showQuickForm.value) {\n form.icon = tempIcon.value\n }\n })\n .catch(error => {\n if (error.response.status !== 422) {\n notify.alert({ text: error.response.data.message})\n }\n })\n }\n\n /**\n * Deletes the temp icon from backend\n */\n function deleteTempIcon() {\n if (isEditMode.value) {\n if (tempIcon.value) {\n if (tempIcon.value !== form.icon) {\n twofaccountService.deleteIcon(tempIcon.value)\n }\n tempIcon.value = ''\n }\n }\n else if (tempIcon.value) {\n twofaccountService.deleteIcon(tempIcon.value)\n tempIcon.value = ''\n if (showQuickForm.value) {\n form.icon = ''\n }\n }\n }\n\n /**\n * Increments the HOTP counter of the form after a preview\n * \n * @param {object} payload \n */\n function incrementHotp(payload) {\n // The quick form or the preview feature has incremented the HOTP counter so we get the new value from\n // the OtpDisplay component.\n // This could desynchronized the HOTP verification server and our local counter if the user never verified the HOTP but this\n // is acceptable (and HOTP counter can be edited by the way)\n form.counter = payload.nextHotpCounter\n \n //form.uri = payload.nextUri\n }\n \n /**\n * Maps errors received by the OtpDisplay to the form errors instance\n * \n * @param {object} errorResponse \n */\n function mapDisplayerErrors(errorResponse) {\n form.errors.set(form.extractErrors(errorResponse))\n }\n\n /**\n * Sends a QR code to backend for decoding and prefill the form with the qr data\n */\n function uploadQrcode() {\n qrcodeForm.qrcode = qrcodeInput.value.files[0]\n\n // First we get the uri encoded in the qrcode\n qrcodeForm.upload('/api/v1/qrcode/decode', { returnError: true })\n .then(response => {\n uri.value = response.data.data\n \n // Then the otp described by the uri\n twofaccountService.preview(uri.value, { returnError: true }).then(response => {\n form.fill(response.data)\n tempIcon.value = response.data.icon ? response.data.icon : null\n })\n .catch(error => {\n if( error.response.status === 422 ) {\n if( error.response.data.errors.uri ) {\n showAlternatives.value = true\n }\n else notify.alert({ text: trans(error.response.data.message) })\n } else {\n notify.error(error)\n }\n })\n })\n .catch(error => {\n if (error.response.status !== 422) {\n notify.alert({ text: error.response.data.message})\n }\n })\n }\n\n /**\n * Tries to get the official logo/icon of the Service filled in the form\n */\n function fetchLogo() {\n if (user.preferences.getOfficialIcons) {\n fetchingLogo.value = true\n\n twofaccountService.getLogo(form.service, { returnError: true })\n .then(response => {\n if (response.status === 201) {\n // clean possible already uploaded temp icon\n deleteTempIcon()\n tempIcon.value = response.data.filename;\n }\n else notify.warn( {text: trans('errors.no_logo_found_for_x', {service: strip_tags(form.service)}) })\n })\n .catch(() => {\n notify.warn({ text: trans('errors.no_logo_found_for_x', {service: strip_tags(form.service)}) })\n })\n .finally(() => {\n fetchingLogo.value = false\n })\n }\n }\n\n /**\n * Strips html tags to prevent code injection\n * \n * @param {*} str \n */\n function strip_tags(str) {\n return str.replace(/(<([^> ]+)>)/ig, \"\")\n }\n\n</script>\n\n<template>\n <UseColorMode v-slot=\"{ mode }\">\n <div>\n <!-- otp display modal -->\n <Modal v-if=\"user.preferences.AutoSaveQrcodedAccount\" v-model=\"showOtpInModal\">\n <OtpDisplay\n ref=\"OtpDisplayForAutoSave\"\n v-bind=\"otpDisplayProps\"\n @please-close-me=\"router.push({ name: 'accounts' })\">\n </OtpDisplay>\n </Modal>\n <!-- Quick form -->\n <form @submit.prevent=\"createAccount\" @keydown=\"form.onKeydown($event)\" v-if=\"!isEditMode && showQuickForm\">\n <div class=\"container preview has-text-centered\">\n <div class=\"columns is-mobile\">\n <div class=\"column\">\n <FieldError v-if=\"iconForm.errors.hasAny('icon')\" :error=\"iconForm.errors.get('icon')\" :field=\"'icon'\" class=\"help-for-file\" />\n <label class=\"add-icon-button\" v-if=\"!tempIcon\">\n <input inert class=\"file-input\" type=\"file\" accept=\"image/*\" v-on:change=\"uploadIcon\" ref=\"iconInput\">\n <FontAwesomeIcon :icon=\"['fas', 'image']\" size=\"2x\" />\n </label>\n <button type=\"button\" class=\"delete delete-icon-button is-medium\" v-if=\"tempIcon\" @click.prevent=\"deleteTempIcon\"></button>\n <OtpDisplay\n ref=\"OtpDisplayForQuickForm\"\n v-bind=\"form.data()\"\n @increment-hotp=\"incrementHotp\"\n @validation-error=\"mapDisplayerErrors\"\n @please-close-me=\"ShowTwofaccountInModal = false\">\n </OtpDisplay>\n </div>\n </div>\n <div class=\"columns is-mobile\" role=\"alert\">\n <div v-if=\"form.errors.any()\" class=\"column\">\n <p v-for=\"(field, index) in form.errors.errors\" :key=\"index\" class=\"help is-danger\">\n <ul>\n <li v-for=\"(error, index) in field\" :key=\"index\">{{ error }}</li>\n </ul>\n </p>\n </div>\n </div>\n <div class=\"columns is-mobile\">\n <div class=\"column quickform-footer\">\n <div class=\"field is-grouped is-grouped-centered\">\n <div class=\"control\">\n <VueButton :isLoading=\"form.isBusy\" >{{ $t('commons.save') }}</VueButton>\n </div>\n <ButtonBackCloseCancel action=\"cancel\" :isText=\"true\" :isRounded=\"false\" :useLinkTag=\"false\" @canceled=\"cancelCreation\" />\n </div>\n </div>\n </div>\n </div>\n </form>\n <!-- Full form -->\n <FormWrapper :title=\"$t(isEditMode ? 'twofaccounts.forms.edit_account' : 'twofaccounts.forms.new_account')\" v-if=\"showAdvancedForm\">\n <form @submit.prevent=\"handleSubmit\" @keydown=\"form.onKeydown($event)\">\n <!-- qcode fileupload -->\n <div v-if=\"!isEditMode\" class=\"field is-grouped\">\n <div class=\"control\">\n <div role=\"button\" tabindex=\"0\" class=\"file is-small\" :class=\"{ 'is-black': mode == 'dark' }\" @keyup.enter=\"qrcodeInputLabel.click()\">\n <label class=\"file-label\" :title=\"$t('twofaccounts.forms.use_qrcode.title')\" ref=\"qrcodeInputLabel\">\n <input inert tabindex=\"-1\" class=\"file-input\" type=\"file\" accept=\"image/*\" v-on:change=\"uploadQrcode\" ref=\"qrcodeInput\">\n <span class=\"file-cta\">\n <span class=\"file-icon\">\n <FontAwesomeIcon :icon=\"['fas', 'qrcode']\" size=\"lg\" />\n </span>\n <span class=\"file-label\">{{ $t('twofaccounts.forms.prefill_using_qrcode') }}</span>\n </span>\n </label>\n </div>\n </div>\n </div>\n <FieldError v-if=\"qrcodeForm.errors.hasAny('qrcode')\" :error=\"qrcodeForm.errors.get('qrcode')\" :field=\"'qrcode'\" class=\"help-for-file\" />\n <!-- service -->\n <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 />\n <!-- account -->\n <FormField v-model=\"form.account\" fieldName=\"account\" :fieldError=\"form.errors.get('account')\" label=\"twofaccounts.account\" :placeholder=\"$t('twofaccounts.forms.account.placeholder')\" />\n <!-- icon upload -->\n <label for=\"filUploadIcon\" class=\"label\">{{ $t('twofaccounts.icon') }}</label>\n <div class=\"field is-grouped\">\n <!-- Try my luck button -->\n <div class=\"control\" v-if=\"user.preferences.getOfficialIcons\">\n <VueButton @click=\"fetchLogo\" :color=\"mode == 'dark' ? 'is-dark' : ''\" :nativeType=\"'button'\" :is-loading=\"fetchingLogo\" :isDisabled=\"!form.service\" aria-describedby=\"lgdTryMyLuck\">\n <span class=\"icon is-small\">\n <FontAwesomeIcon :icon=\"['fas', 'globe']\" />\n </span>\n <span>{{ $t('twofaccounts.forms.i_m_lucky') }}</span>\n </VueButton>\n </div>\n <!-- upload icon button -->\n <div class=\"control is-flex\">\n <div role=\"button\" tabindex=\"0\" class=\"file mr-3\" :class=\"mode == 'dark' ? 'is-dark' : 'is-white'\" @keyup.enter=\"iconInputLabel.click()\">\n <label for=\"filUploadIcon\" class=\"file-label\" ref=\"iconInputLabel\">\n <input id=\"filUploadIcon\" tabindex=\"-1\" class=\"file-input\" type=\"file\" accept=\"image/*\" v-on:change=\"uploadIcon\" ref=\"iconInput\">\n <span class=\"file-cta\">\n <span class=\"file-icon\">\n <FontAwesomeIcon :icon=\"['fas', 'upload']\" />\n </span>\n <span class=\"file-label\">{{ $t('twofaccounts.forms.choose_image') }}</span>\n </span>\n </label>\n </div>\n <span class=\"tag is-large\" :class=\"mode =='dark' ? 'is-dark' : 'is-white'\" v-if=\"tempIcon\">\n <img class=\"icon-preview\" :src=\"$2fauth.config.subdirectory + '/storage/icons/' + tempIcon\" :alt=\"$t('twofaccounts.icon_to_illustrate_the_account')\">\n <button type=\"button\" class=\"clear-selection delete is-small\" @click.prevent=\"deleteTempIcon\" :aria-label=\"$t('twofaccounts.remove_icon')\"></button>\n </span>\n </div>\n </div>\n <div class=\"field\">\n <FieldError v-if=\"iconForm.errors.hasAny('icon')\" :error=\"iconForm.errors.get('icon')\" :field=\"'icon'\" class=\"help-for-file\" />\n <p id=\"lgdTryMyLuck\" v-if=\"user.preferences.getOfficialIcons\" class=\"help\" v-html=\"$t('twofaccounts.forms.i_m_lucky_legend')\"></p>\n </div>\n <!-- group -->\n <FormSelect v-if=\"groups.length > 0\" v-model=\"form.group_id\" :options=\"groups\" fieldName=\"group_id\" label=\"twofaccounts.forms.group.label\" help=\"twofaccounts.forms.group.help\" />\n <!-- otp type -->\n <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\" />\n <div v-if=\"form.otp_type != ''\">\n <!-- secret -->\n <FormLockField :isEditMode=\"isEditMode\" v-model.trimAll=\"form.secret\" fieldName=\"secret\" :fieldError=\"form.errors.get('secret')\" label=\"twofaccounts.forms.secret.label\" help=\"twofaccounts.forms.secret.help\" />\n <!-- Options -->\n <div v-if=\"form.otp_type !== 'steamtotp'\">\n <h2 class=\"title is-4 mt-5 mb-2\">{{ $t('commons.options') }}</h2>\n <p class=\"help mb-4\">\n {{ $t('twofaccounts.forms.options_help') }}\n </p>\n <!-- digits -->\n <FormToggle v-model=\"form.digits\" :choices=\"digitsChoices\" fieldName=\"digits\" :fieldError=\"form.errors.get('digits')\" label=\"twofaccounts.forms.digits.label\" help=\"twofaccounts.forms.digits.help\" />\n <!-- algorithm -->\n <FormToggle v-model=\"form.algorithm\" :choices=\"algorithms\" fieldName=\"algorithm\" :fieldError=\"form.errors.get('algorithm')\" label=\"twofaccounts.forms.algorithm.label\" help=\"twofaccounts.forms.algorithm.help\" />\n <!-- TOTP period -->\n <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')\" />\n <!-- HOTP counter -->\n <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'\" />\n </div>\n </div>\n <VueFooter :showButtons=\"true\">\n <p class=\"control\">\n <VueButton :id=\"isEditMode ? 'btnUpdate' : 'btnCreate'\" :isLoading=\"form.isBusy\" class=\"is-rounded\" >{{ isEditMode ? $t('commons.save') : $t('commons.create') }}</VueButton>\n </p>\n <p class=\"control\" v-if=\"form.otp_type && form.secret\">\n <button id=\"btnPreview\" type=\"button\" class=\"button is-success is-rounded\" @click=\"previewOTP\">{{ $t('twofaccounts.forms.test') }}</button>\n </p>\n <ButtonBackCloseCancel action=\"cancel\" :useLinkTag=\"false\" @canceled=\"cancelCreation\" />\n </VueFooter>\n </form>\n <!-- modal -->\n <modal v-model=\"ShowTwofaccountInModal\">\n <OtpDisplay\n ref=\"OtpDisplayForAdvancedForm\"\n v-bind=\"form.data()\"\n @increment-hotp=\"incrementHotp\"\n @validation-error=\"mapDisplayerErrors\"\n @please-close-me=\"ShowTwofaccountInModal = false\">\n </OtpDisplay>\n </modal>\n </FormWrapper>\n <!-- alternatives -->\n <modal v-model=\"showAlternatives\">\n <QrContentDisplay :qrContent=\"uri\" />\n </modal>\n </div>\n </UseColorMode>\n</template>\n"],"names":["props","__props","inputId","useIdGenerator","valErrorId","useValidationErrorIdGenerator","legendId","fieldIsLocked","ref","hasBeenTrimmed","componentKey","emit","__emit","emitValue","e","value","alertOnSpace","forceRefresh","$2fauth","inject","router","useRouter","route","useRoute","user","useUserStore","twofaccounts","useTwofaccounts","bus","useBusStore","notify","useNotifyStore","form","reactive","Form","qrcodeForm","iconForm","otpDisplayProps","otp_types","digitsChoices","algorithms","uri","tempIcon","showQuickForm","showAlternatives","showOtpInModal","showAdvancedForm","ShowTwofaccountInModal","fetchingLogo","iconInput","OtpDisplayForAutoSave","OtpDisplayForQuickForm","OtpDisplayForAdvancedForm","qrcodeInputLabel","qrcodeInput","iconInputLabel","isEditMode","computed","groups","useGroups","item","trans","onMounted","twofaccountService","response","showOTP","error","nextTick","watch","val","_a","_b","to","from","fetchLogo","deleteTempIcon","handleSubmit","updateAccount","createAccount","data","oldIcon","index","acc","previewOTP","otp","cancelCreation","uploadIcon","incrementHotp","payload","mapDisplayerErrors","errorResponse","uploadQrcode","strip_tags","str"],"mappings":"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"}