aboutsummaryrefslogtreecommitdiff
path: root/extension/src/entries/popup/Components
diff options
context:
space:
mode:
Diffstat (limited to 'extension/src/entries/popup/Components')
-rw-r--r--extension/src/entries/popup/Components/IdentitySelection.vue16
-rw-r--r--extension/src/entries/popup/Components/Login.vue64
-rw-r--r--extension/src/entries/popup/Components/OtpLogin.vue51
-rw-r--r--extension/src/entries/popup/Components/PageContent.vue2
-rw-r--r--extension/src/entries/popup/Components/PassLogin.vue89
5 files changed, 184 insertions, 38 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