Update Account frontend; auto-reload on edit modal change.
This commit is contained in:
parent
1023a104f9
commit
f32286236c
|
@ -114,20 +114,20 @@
|
|||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<account-edit-modal ref="editModal" :user-url="userUrl" :supported-locales="supportedLocales"
|
||||
@relist="relist"></account-edit-modal>
|
||||
<account-edit-modal ref="editmodal" :user-url="userUrl" :supported-locales="supportedLocales"
|
||||
@reload="reload"></account-edit-modal>
|
||||
|
||||
<account-change-password-modal ref="changePasswordModal" :change-password-url="changePasswordUrl"
|
||||
<account-change-password-modal ref="changepasswordmodal" :change-password-url="changePasswordUrl"
|
||||
@relist="relist"></account-change-password-modal>
|
||||
|
||||
<account-two-factor-modal ref="twoFactorModal" :two-factor-url="twoFactorUrl"
|
||||
<account-two-factor-modal ref="twofactormodal" :two-factor-url="twoFactorUrl"
|
||||
@relist="relist"></account-two-factor-modal>
|
||||
|
||||
<account-api-key-modal ref="apiKeyModal" :create-url="apiKeysApiUrl" @relist="relist"></account-api-key-modal>
|
||||
<account-api-key-modal ref="apikeymodal" :create-url="apiKeysApiUrl" @relist="relist"></account-api-key-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import Icon from "~/components/Common/Icon";
|
||||
import DataTable from "~/components/Common/DataTable";
|
||||
import AccountChangePasswordModal from "./Account/ChangePasswordModal";
|
||||
|
@ -137,127 +137,148 @@ import AccountEditModal from "./Account/EditModal";
|
|||
import Avatar from "~/components/Common/Avatar";
|
||||
import InfoCard from "~/components/Common/InfoCard";
|
||||
import EnabledBadge from "~/components/Common/Badges/EnabledBadge.vue";
|
||||
import {onMounted, ref} from "vue";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
||||
|
||||
export default {
|
||||
name: 'Account',
|
||||
components: {
|
||||
EnabledBadge,
|
||||
AccountEditModal,
|
||||
AccountTwoFactorModal,
|
||||
AccountApiKeyModal,
|
||||
AccountChangePasswordModal,
|
||||
Icon,
|
||||
InfoCard,
|
||||
DataTable,
|
||||
Avatar
|
||||
},
|
||||
props: {
|
||||
userUrl: String,
|
||||
changePasswordUrl: String,
|
||||
twoFactorUrl: String,
|
||||
apiKeysApiUrl: String,
|
||||
supportedLocales: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
userLoading: true,
|
||||
user: {
|
||||
name: null,
|
||||
email: null,
|
||||
avatar: {
|
||||
url: null,
|
||||
service: null,
|
||||
serviceUrl: null
|
||||
},
|
||||
roles: [],
|
||||
},
|
||||
securityLoading: true,
|
||||
security: {
|
||||
twoFactorEnabled: false,
|
||||
},
|
||||
apiKeyFields: [
|
||||
{
|
||||
key: 'comment',
|
||||
isRowHeader: true,
|
||||
label: this.$gettext('API Key Description/Comments'),
|
||||
sortable: false
|
||||
},
|
||||
{key: 'actions', label: this.$gettext('Actions'), sortable: false, class: 'shrink'}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.relist();
|
||||
},
|
||||
methods: {
|
||||
relist() {
|
||||
this.userLoading = true;
|
||||
this.$wrapWithLoading(
|
||||
this.axios.get(this.userUrl)
|
||||
).then((resp) => {
|
||||
this.user = {
|
||||
name: resp.data.name,
|
||||
email: resp.data.email,
|
||||
roles: resp.data.roles,
|
||||
avatar: {
|
||||
url: resp.data.avatar.url_64,
|
||||
service: resp.data.avatar.service_name,
|
||||
serviceUrl: resp.data.avatar.service_url
|
||||
}
|
||||
};
|
||||
this.userLoading = false;
|
||||
});
|
||||
const props = defineProps({
|
||||
userUrl: String,
|
||||
changePasswordUrl: String,
|
||||
twoFactorUrl: String,
|
||||
apiKeysApiUrl: String,
|
||||
supportedLocales: Object
|
||||
});
|
||||
|
||||
this.securityLoading = true;
|
||||
this.$wrapWithLoading(
|
||||
this.axios.get(this.twoFactorUrl)
|
||||
).then((resp) => {
|
||||
this.security.twoFactorEnabled = resp.data.two_factor_enabled;
|
||||
this.securityLoading = false;
|
||||
});
|
||||
const userLoading = ref(true);
|
||||
const user = ref({
|
||||
name: null,
|
||||
email: null,
|
||||
avatar: {
|
||||
url: null,
|
||||
service: null,
|
||||
serviceUrl: null
|
||||
},
|
||||
roles: [],
|
||||
});
|
||||
|
||||
this.$refs.datatable.relist();
|
||||
},
|
||||
doEditProfile() {
|
||||
this.$refs.editModal.open();
|
||||
},
|
||||
doChangePassword() {
|
||||
this.$refs.changePasswordModal.open();
|
||||
},
|
||||
enableTwoFactor() {
|
||||
this.$refs.twoFactorModal.open();
|
||||
},
|
||||
disableTwoFactor() {
|
||||
this.$confirmDelete({
|
||||
title: this.$gettext('Disable two-factor authentication?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
this.$wrapWithLoading(
|
||||
this.axios.delete(this.twoFactorUrl)
|
||||
).then((resp) => {
|
||||
this.$notifySuccess(resp.data.message);
|
||||
this.relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
createApiKey() {
|
||||
this.$refs.apiKeyModal.create();
|
||||
},
|
||||
deleteApiKey(url) {
|
||||
this.$confirmDelete({
|
||||
title: this.$gettext('Delete API Key?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
this.$wrapWithLoading(
|
||||
this.axios.delete(url)
|
||||
).then((resp) => {
|
||||
this.$notifySuccess(resp.data.message);
|
||||
this.relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
const securityLoading = ref(true);
|
||||
const security = ref({
|
||||
twoFactorEnabled: false,
|
||||
});
|
||||
|
||||
const {$gettext} = useTranslate();
|
||||
|
||||
const apiKeyFields = [
|
||||
{
|
||||
key: 'comment',
|
||||
isRowHeader: true,
|
||||
label: $gettext('API Key Description/Comments'),
|
||||
sortable: false
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
label: $gettext('Actions'),
|
||||
sortable: false,
|
||||
class: 'shrink'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const datatable = ref(); // DataTable
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const relist = () => {
|
||||
userLoading.value = true;
|
||||
|
||||
wrapWithLoading(
|
||||
axios.get(props.userUrl)
|
||||
).then((resp) => {
|
||||
user.value = {
|
||||
name: resp.data.name,
|
||||
email: resp.data.email,
|
||||
roles: resp.data.roles,
|
||||
avatar: {
|
||||
url: resp.data.avatar.url_64,
|
||||
service: resp.data.avatar.service_name,
|
||||
serviceUrl: resp.data.avatar.service_url
|
||||
}
|
||||
};
|
||||
userLoading.value = false;
|
||||
});
|
||||
|
||||
securityLoading.value = true;
|
||||
|
||||
wrapWithLoading(
|
||||
axios.get(props.twoFactorUrl)
|
||||
).then((resp) => {
|
||||
security.value.twoFactorEnabled = resp.data.two_factor_enabled;
|
||||
securityLoading.value = false;
|
||||
});
|
||||
|
||||
datatable.value?.relist();
|
||||
};
|
||||
|
||||
onMounted(relist);
|
||||
|
||||
const reload = () => {
|
||||
location.reload();
|
||||
};
|
||||
|
||||
const editmodal = ref(); // EditModal
|
||||
|
||||
const doEditProfile = () => {
|
||||
editmodal.value?.open();
|
||||
};
|
||||
|
||||
const changepasswordmodal = ref(); // ChangePasswordModal
|
||||
|
||||
const doChangePassword = () => {
|
||||
changepasswordmodal.value?.open();
|
||||
};
|
||||
|
||||
const twofactormodal = ref(); // TwoFactorModal
|
||||
|
||||
const enableTwoFactor = () => {
|
||||
twofactormodal.value?.open();
|
||||
};
|
||||
|
||||
const {confirmDelete} = useSweetAlert();
|
||||
|
||||
const disableTwoFactor = () => {
|
||||
confirmDelete({
|
||||
title: $gettext('Disable two-factor authentication?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(props.twoFactorUrl)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const apikeymodal = ref(); // ApiKeyModal
|
||||
|
||||
const createApiKey = () => {
|
||||
apikeymodal.value?.create();
|
||||
};
|
||||
|
||||
const deleteApiKey = (url) => {
|
||||
confirmDelete({
|
||||
title: $gettext('Delete API Key?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(url)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<b-modal size="md" centered id="api_keys_modal" ref="modal" :title="langTitle" @hidden="clearContents"
|
||||
<b-modal size="md" centered id="api_keys_modal" ref="modal" :title="$gettext('Add API Key')" @hidden="clearContents"
|
||||
no-enforce-focus>
|
||||
<template #default="slotProps">
|
||||
<b-alert variant="danger" :show="error != null">{{ error }}</b-alert>
|
||||
|
||||
<b-form v-if="newKey === null" class="form vue-form" @submit.prevent="doSubmit">
|
||||
<b-form-fieldset>
|
||||
<b-wrapped-form-group id="form_comments" :field="v$.form.comment" autofocus>
|
||||
<b-wrapped-form-group id="form_comments" :field="v$.comment" autofocus>
|
||||
<template #label>
|
||||
{{ $gettext('API Key Description/Comments') }}
|
||||
</template>
|
||||
|
@ -35,89 +35,79 @@
|
|||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import BFormFieldset from "~/components/Form/BFormFieldset";
|
||||
import InvisibleSubmitButton from "~/components/Common/InvisibleSubmitButton";
|
||||
import AccountApiKeyNewKey from "./ApiKeyNewKey";
|
||||
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
|
||||
import {required} from '@vuelidate/validators';
|
||||
import useVuelidate from "@vuelidate/core";
|
||||
import {ref} from "vue";
|
||||
import {useVuelidateOnForm} from "~/functions/useVuelidateOnForm";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
|
||||
export default {
|
||||
name: 'AccountApiKeyModal',
|
||||
components: {BFormFieldset, InvisibleSubmitButton, AccountApiKeyNewKey, BWrappedFormGroup},
|
||||
props: {
|
||||
createUrl: String
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
v$: useVuelidate()
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
error: null,
|
||||
newKey: null,
|
||||
form: this.getBlankForm()
|
||||
}
|
||||
},
|
||||
validations: {
|
||||
form: {
|
||||
comment: {required}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
langTitle() {
|
||||
return this.$gettext('Add API Key');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
create() {
|
||||
this.resetForm();
|
||||
this.error = null;
|
||||
const props = defineProps({
|
||||
createUrl: String
|
||||
});
|
||||
|
||||
this.$refs.modal.show();
|
||||
},
|
||||
getBlankForm() {
|
||||
return {
|
||||
comment: ''
|
||||
};
|
||||
},
|
||||
resetForm() {
|
||||
this.newKey = null;
|
||||
this.form = this.getBlankForm();
|
||||
},
|
||||
doSubmit() {
|
||||
this.v$.$touch();
|
||||
if (this.v$.$errors.length > 0) {
|
||||
return;
|
||||
}
|
||||
const emit = defineEmits(['relist']);
|
||||
|
||||
this.error = null;
|
||||
const error = ref(null);
|
||||
const newKey = ref(null);
|
||||
|
||||
this.$wrapWithLoading(
|
||||
this.axios({
|
||||
method: 'POST',
|
||||
url: this.createUrl,
|
||||
data: this.form
|
||||
})
|
||||
).then((resp) => {
|
||||
this.newKey = resp.data.key;
|
||||
this.$emit('relist');
|
||||
}).catch((error) => {
|
||||
this.error = error.response.data.message;
|
||||
});
|
||||
},
|
||||
close() {
|
||||
this.$refs.modal.hide();
|
||||
this.clearContents();
|
||||
},
|
||||
clearContents() {
|
||||
this.v$.$reset();
|
||||
|
||||
this.error = null;
|
||||
this.resetForm();
|
||||
},
|
||||
const {form, resetForm, v$} = useVuelidateOnForm(
|
||||
{
|
||||
comment: {required}
|
||||
},
|
||||
{
|
||||
comment: ''
|
||||
}
|
||||
);
|
||||
|
||||
const clearContents = () => {
|
||||
resetForm();
|
||||
error.value = null;
|
||||
newKey.value = null;
|
||||
};
|
||||
|
||||
const modal = ref(); // BModal
|
||||
|
||||
const create = () => {
|
||||
clearContents();
|
||||
|
||||
modal.value?.show();
|
||||
};
|
||||
|
||||
const {wrapWithLoading} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doSubmit = () => {
|
||||
v$.value.$touch();
|
||||
if (v$.value.$errors.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
error.value = null;
|
||||
|
||||
wrapWithLoading(
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: props.createUrl,
|
||||
data: form.value
|
||||
})
|
||||
).then((resp) => {
|
||||
newKey.value = resp.data.key;
|
||||
emit('relist');
|
||||
}).catch((error) => {
|
||||
error.value = error.response.data.message;
|
||||
});
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
modal.value?.hide();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
create
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -63,59 +63,60 @@
|
|||
</b-form-fieldset>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
|
||||
import BFormFieldset from "~/components/Form/BFormFieldset";
|
||||
import objectToFormOptions from "~/functions/objectToFormOptions";
|
||||
import {computed} from "vue";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
|
||||
export default {
|
||||
name: 'AccountEditForm',
|
||||
props: {
|
||||
form: Object,
|
||||
supportedLocales: Object
|
||||
},
|
||||
components: {BFormFieldset, BWrappedFormGroup},
|
||||
computed: {
|
||||
localeOptions() {
|
||||
let localeOptions = objectToFormOptions(this.supportedLocales);
|
||||
localeOptions.unshift({
|
||||
text: this.$gettext('Use Browser Default'),
|
||||
value: 'default'
|
||||
});
|
||||
return localeOptions;
|
||||
const props = defineProps({
|
||||
form: Object,
|
||||
supportedLocales: Object
|
||||
});
|
||||
|
||||
const {$gettext} = useTranslate();
|
||||
|
||||
const localeOptions = computed(() => {
|
||||
let localeOptions = objectToFormOptions(props.supportedLocales);
|
||||
localeOptions.unshift({
|
||||
text: $gettext('Use Browser Default'),
|
||||
value: 'default'
|
||||
});
|
||||
return localeOptions;
|
||||
});
|
||||
|
||||
const themeOptions = computed(() => {
|
||||
return [
|
||||
{
|
||||
text: $gettext('Prefer System Default'),
|
||||
value: 'browser'
|
||||
},
|
||||
themeOptions() {
|
||||
return [
|
||||
{
|
||||
text: this.$gettext('Prefer System Default'),
|
||||
value: 'browser'
|
||||
},
|
||||
{
|
||||
text: this.$gettext('Light'),
|
||||
value: 'light'
|
||||
},
|
||||
{
|
||||
text: this.$gettext('Dark'),
|
||||
value: 'dark'
|
||||
}
|
||||
];
|
||||
{
|
||||
text: $gettext('Light'),
|
||||
value: 'light'
|
||||
},
|
||||
show24hourOptions() {
|
||||
return [
|
||||
{
|
||||
text: this.$gettext('Prefer System Default'),
|
||||
value: null
|
||||
},
|
||||
{
|
||||
text: this.$gettext('12 Hour'),
|
||||
value: false
|
||||
},
|
||||
{
|
||||
text: this.$gettext('24 Hour'),
|
||||
value: true
|
||||
}
|
||||
];
|
||||
{
|
||||
text: $gettext('Dark'),
|
||||
value: 'dark'
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
const show24hourOptions = computed(() => {
|
||||
return [
|
||||
{
|
||||
text: $gettext('Prefer System Default'),
|
||||
value: null
|
||||
},
|
||||
{
|
||||
text: $gettext('12 Hour'),
|
||||
value: false
|
||||
},
|
||||
{
|
||||
text: $gettext('24 Hour'),
|
||||
value: true
|
||||
}
|
||||
];
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,121 +1,105 @@
|
|||
<template>
|
||||
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.$invalid"
|
||||
<modal-form ref="modal" :loading="loading" :title="$gettext('Edit Profile')" :error="error"
|
||||
:disable-save-button="v$.$invalid"
|
||||
@submit="doSubmit" @hidden="clearContents">
|
||||
|
||||
<account-edit-form :form="v$.form" :supported-locales="supportedLocales"></account-edit-form>
|
||||
<account-edit-form :form="v$" :supported-locales="supportedLocales"></account-edit-form>
|
||||
|
||||
</modal-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import mergeExisting from "~/functions/mergeExisting";
|
||||
import {email, required} from '@vuelidate/validators';
|
||||
import useVuelidate from "@vuelidate/core";
|
||||
import AccountEditForm from "./EditForm.vue";
|
||||
import ModalForm from "~/components/Common/ModalForm.vue";
|
||||
import {ref} from "vue";
|
||||
import {useVuelidateOnForm} from "~/functions/useVuelidateOnForm";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
|
||||
export default {
|
||||
name: 'AccountEditModal',
|
||||
components: {ModalForm, AccountEditForm},
|
||||
emits: ['relist'],
|
||||
props: {
|
||||
userUrl: String,
|
||||
supportedLocales: Object
|
||||
const props = defineProps({
|
||||
userUrl: String,
|
||||
supportedLocales: Object
|
||||
});
|
||||
|
||||
const emit = defineEmits(['reload']);
|
||||
|
||||
const loading = ref(true);
|
||||
const error = ref(null);
|
||||
|
||||
const {form, resetForm, v$} = useVuelidateOnForm(
|
||||
{
|
||||
name: {},
|
||||
email: {required, email},
|
||||
locale: {required},
|
||||
theme: {required},
|
||||
show_24_hour_time: {}
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
v$: useVuelidate()
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
error: null,
|
||||
form: {
|
||||
...this.getBlankForm(),
|
||||
}
|
||||
};
|
||||
},
|
||||
validations() {
|
||||
return {
|
||||
form: {
|
||||
name: {},
|
||||
email: {required, email},
|
||||
locale: {required},
|
||||
theme: {required},
|
||||
show_24_hour_time: {}
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
langTitle() {
|
||||
return this.$gettext('Edit Profile');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getBlankForm() {
|
||||
return {
|
||||
name: '',
|
||||
email: '',
|
||||
locale: 'default',
|
||||
theme: 'browser',
|
||||
show_24_hour_time: null,
|
||||
};
|
||||
},
|
||||
resetForm() {
|
||||
this.form = {
|
||||
...this.getBlankForm()
|
||||
};
|
||||
},
|
||||
open() {
|
||||
this.resetForm();
|
||||
this.loading = false;
|
||||
this.error = null;
|
||||
|
||||
this.$refs.modal.show();
|
||||
|
||||
this.$wrapWithLoading(
|
||||
this.axios.get(this.userUrl)
|
||||
).then((resp) => {
|
||||
this.form = mergeExisting(this.form, resp.data);
|
||||
this.loading = false;
|
||||
}).catch(() => {
|
||||
this.close();
|
||||
});
|
||||
},
|
||||
doSubmit() {
|
||||
this.v$.$touch();
|
||||
if (this.v$.$errors.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.error = null;
|
||||
|
||||
this.$wrapWithLoading(
|
||||
this.axios({
|
||||
method: 'PUT',
|
||||
url: this.userUrl,
|
||||
data: this.form
|
||||
})
|
||||
).then(() => {
|
||||
this.$notifySuccess();
|
||||
this.$emit('relist');
|
||||
this.close();
|
||||
}).catch((error) => {
|
||||
this.error = error.response.data.message;
|
||||
});
|
||||
},
|
||||
close() {
|
||||
this.$refs.modal.hide();
|
||||
},
|
||||
clearContents() {
|
||||
this.v$.$reset();
|
||||
|
||||
this.loading = false;
|
||||
this.error = null;
|
||||
|
||||
this.resetForm();
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
email: '',
|
||||
locale: 'default',
|
||||
theme: 'browser',
|
||||
show_24_hour_time: null,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const clearContents = () => {
|
||||
resetForm();
|
||||
loading.value = false;
|
||||
error.value = null;
|
||||
};
|
||||
|
||||
const modal = ref(); // BModal
|
||||
|
||||
const close = () => {
|
||||
modal.value.hide();
|
||||
};
|
||||
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const open = () => {
|
||||
clearContents();
|
||||
|
||||
modal.value?.show();
|
||||
|
||||
wrapWithLoading(
|
||||
axios.get(props.userUrl)
|
||||
).then((resp) => {
|
||||
form.value = mergeExisting(form.value, resp.data);
|
||||
loading.value = false;
|
||||
}).catch(() => {
|
||||
close();
|
||||
});
|
||||
};
|
||||
|
||||
const doSubmit = () => {
|
||||
v$.value.$touch();
|
||||
if (v$.value.$errors.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
error.value = null;
|
||||
|
||||
wrapWithLoading(
|
||||
axios({
|
||||
method: 'PUT',
|
||||
url: props.userUrl,
|
||||
data: form.value
|
||||
})
|
||||
).then(() => {
|
||||
notifySuccess();
|
||||
emit('reload');
|
||||
close();
|
||||
}).catch((error) => {
|
||||
error.value = error.response.data.message;
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.$invalid"
|
||||
<modal-form ref="modal" :loading="loading" :title="$gettext('Enable Two-Factor Authentication')"
|
||||
:error="error" :disable-save-button="v$.$invalid"
|
||||
@submit="doSubmit" @hidden="clearContents" no-enforce-focus>
|
||||
|
||||
<b-row>
|
||||
|
@ -23,7 +24,7 @@
|
|||
</p>
|
||||
|
||||
<b-form-fieldset>
|
||||
<b-wrapped-form-group id="form_otp" :field="v$.form.otp" autofocus>
|
||||
<b-wrapped-form-group id="form_otp" :field="v$.otp" autofocus>
|
||||
<template #label>
|
||||
{{ $gettext('Code from Authenticator App') }}
|
||||
</template>
|
||||
|
@ -51,116 +52,106 @@
|
|||
</modal-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import ModalForm from "~/components/Common/ModalForm";
|
||||
import CopyToClipboardButton from "~/components/Common/CopyToClipboardButton";
|
||||
import BFormFieldset from "~/components/Form/BFormFieldset";
|
||||
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
|
||||
import {minLength, required} from "@vuelidate/validators";
|
||||
import useVuelidate from "@vuelidate/core";
|
||||
import {useVuelidateOnForm} from "~/functions/useVuelidateOnForm";
|
||||
import {ref} from "vue";
|
||||
import {useResettableRef} from "~/functions/useResettableRef";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
|
||||
export default {
|
||||
name: 'AccountTwoFactorModal',
|
||||
components: {ModalForm, CopyToClipboardButton, BFormFieldset, BWrappedFormGroup},
|
||||
emits: ['relist'],
|
||||
props: {
|
||||
twoFactorUrl: String
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
v$: useVuelidate()
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
error: null,
|
||||
totp: {
|
||||
secret: null,
|
||||
totp_uri: null,
|
||||
qr_code: null
|
||||
},
|
||||
form: {
|
||||
otp: ''
|
||||
}
|
||||
};
|
||||
},
|
||||
validations: {
|
||||
form: {
|
||||
otp: {
|
||||
required,
|
||||
minLength: minLength(6)
|
||||
}
|
||||
const props = defineProps({
|
||||
twoFactorUrl: String
|
||||
});
|
||||
|
||||
const emit = defineEmits(['relist']);
|
||||
|
||||
const loading = ref(true);
|
||||
const error = ref(null);
|
||||
|
||||
const {form, resetForm, v$} = useVuelidateOnForm(
|
||||
{
|
||||
otp: {
|
||||
required,
|
||||
minLength: minLength(6)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
langTitle() {
|
||||
return this.$gettext('Enable Two-Factor Authentication');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetForm() {
|
||||
this.totp = {
|
||||
secret: null,
|
||||
totp_uri: null,
|
||||
qr_code: null
|
||||
};
|
||||
this.form = {
|
||||
otp: '',
|
||||
};
|
||||
},
|
||||
open() {
|
||||
this.resetForm();
|
||||
this.loading = false;
|
||||
this.error = null;
|
||||
|
||||
this.$refs.modal.show();
|
||||
|
||||
this.$wrapWithLoading(
|
||||
this.axios.put(this.twoFactorUrl)
|
||||
).then((resp) => {
|
||||
this.totp = resp.data;
|
||||
this.loading = false;
|
||||
}).catch(() => {
|
||||
this.close();
|
||||
});
|
||||
},
|
||||
doSubmit() {
|
||||
this.v$.$touch();
|
||||
if (this.v$.$errors.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.error = null;
|
||||
|
||||
this.$wrapWithLoading(
|
||||
this.axios({
|
||||
method: 'PUT',
|
||||
url: this.twoFactorUrl,
|
||||
data: {
|
||||
secret: this.totp.secret,
|
||||
otp: this.form.otp
|
||||
}
|
||||
})
|
||||
).then(() => {
|
||||
this.$notifySuccess();
|
||||
this.$emit('relist');
|
||||
this.close();
|
||||
}).catch((error) => {
|
||||
this.error = error.response.data.message;
|
||||
});
|
||||
},
|
||||
close() {
|
||||
this.$refs.modal.hide();
|
||||
},
|
||||
clearContents() {
|
||||
this.v$.$reset();
|
||||
|
||||
this.loading = false;
|
||||
this.error = null;
|
||||
|
||||
this.resetForm();
|
||||
},
|
||||
{
|
||||
otp: ''
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const {record: totp, reset: resetTotp} = useResettableRef({
|
||||
secret: null,
|
||||
totp_uri: null,
|
||||
qr_code: null
|
||||
});
|
||||
|
||||
const clearContents = () => {
|
||||
resetForm();
|
||||
resetTotp();
|
||||
|
||||
loading.value = false;
|
||||
error.value = null;
|
||||
};
|
||||
|
||||
const modal = ref(); // BModal
|
||||
|
||||
const close = () => {
|
||||
modal.value?.hide();
|
||||
};
|
||||
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const open = () => {
|
||||
clearContents();
|
||||
|
||||
loading.value = true;
|
||||
|
||||
modal.value?.show();
|
||||
|
||||
wrapWithLoading(
|
||||
axios.put(props.twoFactorUrl)
|
||||
).then((resp) => {
|
||||
totp.value = resp.data;
|
||||
loading.value = false;
|
||||
}).catch(() => {
|
||||
close();
|
||||
});
|
||||
};
|
||||
|
||||
const doSubmit = () => {
|
||||
v$.value.$touch();
|
||||
if (v$.value.$errors.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
error.value = null;
|
||||
|
||||
wrapWithLoading(
|
||||
axios({
|
||||
method: 'PUT',
|
||||
url: props.twoFactorUrl,
|
||||
data: {
|
||||
secret: totp.value.secret,
|
||||
otp: form.value.otp
|
||||
}
|
||||
})
|
||||
).then(() => {
|
||||
notifySuccess();
|
||||
emit('relist');
|
||||
close();
|
||||
}).catch((error) => {
|
||||
error.value = error.response.data.message;
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
});
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue