diff options
Diffstat (limited to 'extension/src/entries/popup')
-rw-r--r-- | extension/src/entries/popup/Components/IdentitySelection.vue | 16 | ||||
-rw-r--r-- | extension/src/entries/popup/Components/Login.vue | 64 | ||||
-rw-r--r-- | extension/src/entries/popup/Components/OtpLogin.vue | 51 | ||||
-rw-r--r-- | extension/src/entries/popup/Components/PageContent.vue | 2 | ||||
-rw-r--r-- | extension/src/entries/popup/Components/PassLogin.vue | 89 | ||||
-rw-r--r-- | extension/src/entries/popup/main.js | 4 |
6 files changed, 186 insertions, 40 deletions
diff --git a/extension/src/entries/popup/Components/IdentitySelection.vue b/extension/src/entries/popup/Components/IdentitySelection.vue index 99d8e34..eb08fb1 100644 --- a/extension/src/entries/popup/Components/IdentitySelection.vue +++ b/extension/src/entries/popup/Components/IdentitySelection.vue @@ -1,18 +1,22 @@ <template> <div class="text-left"> - <div class="w-full"> - <div class=""> + <div class="flex flex-row w-full gap-1"> + <div class="flex-1"> <select class="w-full input" - :disabled="waiting" - :value="selected?.Id" - @change.prevent="onSelected" + :disabled="waiting" + :value="selected?.Id" + @change.prevent="onSelected" > <option disabled value="">Select an identity</option> <option v-for="key in allKeys" :value="key.Id">{{ key.UserName }}</option> </select> </div> + <div class="my-auto"> + <button class="btn sm borderless" @click="store.refreshIdentities()"> + <fa-icon icon="refresh" class="" /> + </button> + </div> </div> - </div> </template> diff --git a/extension/src/entries/popup/Components/Login.vue b/extension/src/entries/popup/Components/Login.vue index 93c0178..8b7b807 100644 --- a/extension/src/entries/popup/Components/Login.vue +++ b/extension/src/entries/popup/Components/Login.vue @@ -1,44 +1,46 @@ <template> <div id="login-template" class="py-4"> - <form class="" @submit.prevent="onSubmit"> - <fieldset class="px-4 input-container"> - <label class="">Please enter your authentication token</label> - <textarea class="w-full input" v-model="token" rows="5"> - </textarea> - </fieldset> - <div class="flex justify-end mt-2"> - <div class="px-3"> - <button class="w-24 rounded btn sm primary"> - <fa-icon v-if="waiting" icon="spinner" class="animate-spin" /> - <span v-else>Submit</span> - </button> - </div> - </div> - </form> + <TabGroup @change="onChange"> + <TabList as="div" class="flex flex-row mx-auto mb-3 font-bold w-fit"> + <Tab + class="p-0.5 mx-1 border-b" + :class="[ isActive(0) ? 'border-gray-400' : 'border-transparent']" + > + OTP + </Tab> + <Tab + class="p-0.5 mx-1 border-b" + :class="[ isActive(1) ? 'border-gray-400' : 'border-transparent']" + > + User/Pass + </Tab> + </TabList> + <TabPanels> + <TabPanel> + <OtpLogin /> + </TabPanel> + <TabPanel> + <PassLogin /> + </TabPanel> + </TabPanels> + </TabGroup> </div> </template> <script setup lang="ts"> -import { apiCall, useWait } from "@vnuge/vnlib.browser"; -import { ref } from "vue"; -import { useStore } from "../../store"; +import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue' +import PassLogin from "./PassLogin.vue"; +import OtpLogin from "./OtpLogin.vue"; +import { shallowRef } from 'vue'; -const { login } = useStore() -const { waiting } = useWait() +const activeTab = shallowRef(0) -const token = ref('') - -const onSubmit = async () => { - await apiCall(async ({ toaster }) => { - await login(token.value) - toaster.form.success({ - 'title': 'Login successful', - 'text': 'Successfully logged into your profile' - }) - }) - +const onChange = (index: any) => { + activeTab.value = index } +const isActive = (index: any) => activeTab.value === index + </script> <style lang="scss"> diff --git a/extension/src/entries/popup/Components/OtpLogin.vue b/extension/src/entries/popup/Components/OtpLogin.vue new file mode 100644 index 0000000..a2b8ac7 --- /dev/null +++ b/extension/src/entries/popup/Components/OtpLogin.vue @@ -0,0 +1,51 @@ +<template> + <form class="" @submit.prevent="onSubmit"> + <fieldset class="px-4 input-container"> + <label class="">Please enter your authentication token</label> + <textarea class="w-full input" v-model="token" rows="5" /> + </fieldset> + <div class="flex justify-end mt-2"> + <div class="px-3"> + <button class="w-24 rounded btn sm primary"> + <fa-icon v-if="waiting" icon="spinner" class="animate-spin" /> + <span v-else>Submit</span> + </button> + </div> + </div> + </form> +</template> + +<script setup lang="ts"> +import { apiCall, useWait } from "@vnuge/vnlib.browser"; +import { ref } from "vue"; +import { useStore } from "../../store"; + +const { login } = useStore() +const { waiting } = useWait() + +const token = ref('') + +const onSubmit = async () => { + await apiCall(async ({ toaster }) => { + try{ + await login(token.value) + + toaster.form.success({ + 'title': 'Login successful', + 'text': 'Successfully logged into your profile' + }) + } + catch(e:any){ + if('response' in e){ + throw e; + } + + toaster.form.error({ + title: 'Failed to login', + text: e.message + }) + } + }) +} + +</script>
\ No newline at end of file diff --git a/extension/src/entries/popup/Components/PageContent.vue b/extension/src/entries/popup/Components/PageContent.vue index e4fcb49..8a48840 100644 --- a/extension/src/entries/popup/Components/PageContent.vue +++ b/extension/src/entries/popup/Components/PageContent.vue @@ -57,7 +57,7 @@ {{ pubKey ?? 'No key selected' }} </div> <div class="my-auto ml-auto cursor-pointer" :class="{'text-primary-500': copied }"> - <fa-icon class="mr-1" icon="copy" @click="copy(pubKey)"/> + <fa-icon class="mr-1" icon="copy" @click="copy(pubKey!)"/> </div> </div> </div> diff --git a/extension/src/entries/popup/Components/PassLogin.vue b/extension/src/entries/popup/Components/PassLogin.vue new file mode 100644 index 0000000..29aeeb6 --- /dev/null +++ b/extension/src/entries/popup/Components/PassLogin.vue @@ -0,0 +1,89 @@ +<template> + <div v-if="showTotp" class=""> + <form id="totp-login" class="" @submit.prevent=""> + <fieldset class="px-4 input-container"> + <div class="text-center"> + <label class="text-sm text-center">Enter your totp code</label> + <div class="m-auto mt-3 w-min"> + <VOtpInput + class="otp-input" + input-type="letter-numeric" + separator="" + value="" + input-classes="primary input rounded" + :num-inputs="6" + @on-complete="onSubmitTotp" + /> + </div> + </div> + </fieldset> + </form> + </div> + <div v-else> + <form class="" @submit.prevent="onSubmit()"> + <fieldset class="px-4 input-container"> + <div class=""> + <label class="">Username</label> + <input type="text" name="username" class="w-full input" v-model="username" /> + </div> + <div class="mt-1"> + <label class="">Password</label> + <input type="password" name="password" class="w-full input" v-model="password" /> + </div> + </fieldset> + <div class="flex justify-end mt-2"> + <div class="px-3"> + <button class="w-24 rounded btn sm primary"> + <fa-icon v-if="waiting" icon="spinner" class="animate-spin" /> + <span v-else>Submit</span> + </button> + </div> + </div> + </form> + </div> +</template> + +<script setup lang="ts"> +import { useStore } from '../../store'; +import { computed, shallowRef } from 'vue'; +import { apiCall, useWait } from "@vnuge/vnlib.browser"; +import { isEmpty, toNumber } from 'lodash'; +import VOtpInput from "vue3-otp-input"; + +const { waiting } = useWait() +const store = useStore(); + +const showTotp = computed(() => store.mfaStatus?.type === 'totp') + +const username = shallowRef(''); +const password = shallowRef(''); + +const onSubmit = () => { + + //Invoke user-pass login + apiCall(async ({ toaster }) => { + + //Validate + if(isEmpty(username.value) || isEmpty(password.value)) { + toaster.form.error({ + title:'Please enter your username and password' + }) + return + } + + await store.login(username.value, password.value) + }); +}; + +const onSubmitTotp = (code: string) => { + //Invoke totp login + apiCall(() => store.plugins.user.submitMfa({ code: toNumber(code) })); +}; + +</script> + +<style lang="scss"> + #totp-login .otp-input input { + @apply w-10 p-0.5 rounded text-center text-lg mx-1 focus:border-primary-500; +} +</style>
\ No newline at end of file diff --git a/extension/src/entries/popup/main.js b/extension/src/entries/popup/main.js index 8b8a3d9..c8e9ef8 100644 --- a/extension/src/entries/popup/main.js +++ b/extension/src/entries/popup/main.js @@ -24,10 +24,10 @@ import "./local.scss" /* FONT AWESOME CONFIG */ import { library } from '@fortawesome/fontawesome-svg-core' -import { faArrowRightFromBracket, faCopy, faEdit, faGear, faMinus, faMoon, faPlus, faSpinner, faSun } from '@fortawesome/free-solid-svg-icons' +import { faArrowRightFromBracket, faCopy, faEdit, faGear, faMinus, faMoon, faPlus, faRefresh, faSpinner, faSun } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' -library.add(faSpinner, faEdit, faGear, faCopy, faArrowRightFromBracket, faPlus, faMinus, faSun, faMoon) +library.add(faSpinner, faEdit, faGear, faCopy, faArrowRightFromBracket, faPlus, faMinus, faSun, faMoon, faRefresh) const bgPlugin = useBackgroundPiniaPlugin('popup') |