aboutsummaryrefslogtreecommitdiff
path: root/front-end/src/components/Settings
diff options
context:
space:
mode:
Diffstat (limited to 'front-end/src/components/Settings')
-rw-r--r--front-end/src/components/Settings/Bookmarks.vue119
-rw-r--r--front-end/src/components/Settings/Registation.vue130
2 files changed, 205 insertions, 44 deletions
diff --git a/front-end/src/components/Settings/Bookmarks.vue b/front-end/src/components/Settings/Bookmarks.vue
index 7f73921..a4ab55a 100644
--- a/front-end/src/components/Settings/Bookmarks.vue
+++ b/front-end/src/components/Settings/Bookmarks.vue
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { apiCall, useWait } from '@vnuge/vnlib.browser';
-import { useStore, type DownloadContentType } from '../../store';
+import { useStore, type DownloadContentType, TabId } from '../../store';
import { ref } from 'vue';
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
@@ -36,52 +36,83 @@ const downloadBookmarks = (contentType: DownloadContentType) => {
});
}
+const bookmarkHref = `
+javascript: (function() {
+ const bookmarkUrl = window.location;
+ let applicationUrl = '${window.location.origin}';
+ applicationUrl += '?tab=${TabId.Bookmarks}&url=' + encodeURIComponent(bookmarkUrl);
+ applicationUrl += '&title='+document.title;
+ window.open(applicationUrl);
+})();`
+
</script>
<template>
- <div class="relative w-fit">
+ <div class="flex flex-col gap-4">
+ <div class="flex-row hidden gap-2 sm:flex">
+ <div class="">
+ <a :href="bookmarkHref" @click.prevent="" class="text-sm cursor-move btn light">
+ <span class="whitespace-nowrap">
+ Add Bookmark 📎
+ </span>
+ </a>
+ </div>
+ <p class="p-0.5 my-auto text-sm flex flex-row">
+ <span class="">
+ <svg class="w-6 h-5 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12l4-4m-4 4 4 4"/>
+ </svg>
+ </span>
+ <span>
+ Drag this button to your bookmarks bar to quickly add a new bookmark
+ </span>
+ </p>
+ </div>
+ <div class="relative ml-auto sm:ml-0 w-fit">
<Menu>
- <MenuButton :disabled="waiting" class="flex items-center gap-3 btn light">
- <div class="hidden lg:inline">Download</div>
- <svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none">
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 13V4M7 14H5a1 1 0 0 0-1 1v4c0 .6.4 1 1 1h14c.6 0 1-.4 1-1v-4c0-.6-.4-1-1-1h-2m-1-5-4 5-4-5m9 8h0"/>
- </svg>
- </MenuButton>
- <transition
- enter-active-class="transition duration-100 ease-out"
- enter-from-class="transform scale-95 opacity-0"
- enter-to-class="transform scale-100 opacity-100"
- leave-active-class="transition duration-75 ease-out"
- leave-from-class="transform scale-100 opacity-100"
- leave-to-class="transform scale-95 opacity-0"
- >
- <MenuItems class="absolute z-10 bg-white divide-y divide-gray-100 rounded-b shadow right-2 lg:left-0 min-w-32 lg:end-0 dark:bg-gray-700">
- <ul class="py-2 text-sm text-gray-700 dark:text-gray-200" aria-labelledby="dropdownDefaultButton">
- <!-- Use the `active` state to conditionally style the active item. -->
- <MenuItem as="template" v-slot="{ }">
- <li>
- <button @click="downloadBookmarks('text/html')" class="block w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white">
- HTML
- </button>
- </li>
- </MenuItem>
- <MenuItem as="template" v-slot="{ }">
- <li>
- <button @click="downloadBookmarks('text/csv')" class="block w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white">
- CSV
- </button>
- </li>
- </MenuItem>
- <MenuItem as="template" v-slot="{ }">
- <li>
- <button @click="downloadBookmarks('application/json')" class="block w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white">
- JSON
- </button>
- </li>
- </MenuItem>
- </ul>
- </MenuItems>
- </transition>
- </Menu>
+ <MenuButton :disabled="waiting" class="flex items-center gap-3 btn light">
+ <div class="hidden lg:inline">Download</div>
+ <svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none">
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 13V4M7 14H5a1 1 0 0 0-1 1v4c0 .6.4 1 1 1h14c.6 0 1-.4 1-1v-4c0-.6-.4-1-1-1h-2m-1-5-4 5-4-5m9 8h0"/>
+ </svg>
+ </MenuButton>
+ <transition
+ enter-active-class="transition duration-100 ease-out"
+ enter-from-class="transform scale-95 opacity-0"
+ enter-to-class="transform scale-100 opacity-100"
+ leave-active-class="transition duration-75 ease-out"
+ leave-from-class="transform scale-100 opacity-100"
+ leave-to-class="transform scale-95 opacity-0"
+ >
+ <MenuItems class="absolute z-10 bg-white divide-y divide-gray-100 rounded-b shadow right-2 lg:left-0 min-w-32 lg:end-0 dark:bg-gray-700">
+ <ul class="py-2 text-sm text-gray-700 dark:text-gray-200" aria-labelledby="dropdownDefaultButton">
+ <!-- Use the `active` state to conditionally style the active item. -->
+ <MenuItem as="template" v-slot="{ }">
+ <li>
+ <button @click="downloadBookmarks('text/html')" class="block w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white">
+ HTML
+ </button>
+ </li>
+ </MenuItem>
+ <MenuItem as="template" v-slot="{ }">
+ <li>
+ <button @click="downloadBookmarks('text/csv')" class="block w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white">
+ CSV
+ </button>
+ </li>
+ </MenuItem>
+ <MenuItem as="template" v-slot="{ }">
+ <li>
+ <button @click="downloadBookmarks('application/json')" class="block w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white">
+ JSON
+ </button>
+ </li>
+ </MenuItem>
+ </ul>
+ </MenuItems>
+ </transition>
+ </Menu>
+ </div>
</div>
+
<a ref="downloadAnchor" class="hidden"></a>
</template> \ No newline at end of file
diff --git a/front-end/src/components/Settings/Registation.vue b/front-end/src/components/Settings/Registation.vue
new file mode 100644
index 0000000..a0f208e
--- /dev/null
+++ b/front-end/src/components/Settings/Registation.vue
@@ -0,0 +1,130 @@
+<script setup lang="ts">
+import { computed, defineAsyncComponent, ref, shallowReactive, shallowRef } from 'vue';
+import { useStore } from '../../store';
+import { get, set, useClipboard, useTimeAgo, useToggle } from '@vueuse/core';
+import { apiCall, useVuelidateWrapper } from '@vnuge/vnlib.browser';
+import { defaultTo } from 'lodash-es';
+import { useVuelidate } from '@vuelidate/core'
+import { required, maxLength, minLength, helpers, email } from '@vuelidate/validators'
+const Dialog = defineAsyncComponent(() => import('../global/Dialog.vue'));
+
+const store = useStore();
+const { copy, copied } = useClipboard()
+
+const [isOpen, toggleOpen] = useToggle(false)
+const vState = shallowReactive({ email: '' })
+
+const canInvite = ref<boolean>(false)
+const inviteLink = shallowRef<string | undefined>()
+
+const expirationDate = computed(() => Date.now() + (1000 * defaultTo(store.registation.status?.link_expiration, 0)))
+const fromNowTime = useTimeAgo(expirationDate)
+
+const rules = computed(() => {
+ return {
+ email: {
+ required: helpers.withMessage('Email is required', required),
+ maxLength: helpers.withMessage('Email must be less than 255 characters', maxLength(255)),
+ minLength: helpers.withMessage('Email must be at least 3 characters', minLength(3)),
+ email: helpers.withMessage('Email must be a valid email', email)
+ },
+ }
+})
+
+const v$ = useVuelidate(rules, vState)
+const { validate } = useVuelidateWrapper(v$ as any)
+
+const onSubmit = async () => {
+ if (!await validate()) return
+
+ apiCall(async () => {
+ //Rest password and pass totp code
+ const { link } = await store.registation.api.createSignupLink(vState.email, get(canInvite))
+ set(inviteLink, link)
+ })
+}
+
+const onCancel = () => {
+ vState.email = ''
+ v$.value.$reset()
+ set(canInvite, false)
+ set(inviteLink, undefined)
+ toggleOpen(false)
+}
+
+</script>
+
+<template>
+ <div class="">
+
+ <div class="flex flex-row justify-between w-full">
+ <h3 class="text-xl font-bold">Registation</h3>
+
+ <div class="flex flex-row justify-end">
+ <button class="btn blue" @click="toggleOpen(true)">Invite User</button>
+ </div>
+ </div>
+ <p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
+ Create a one-time invite link you can send to add a new user to your server.
+ </p>
+ <p class="text-sm text-gray-500 dark:text-gray-400">
+ Links expire <span class="text-blue-500">{{ fromNowTime }}</span>
+ </p>
+ <Dialog :open="isOpen" title="Invite User" @cancel="onCancel">
+ <template #body>
+ <div class="p-4">
+
+ <div v-if="inviteLink" class="">
+ <p class="my-2 text-lg font-medium text-center text-gray-900 dark:text-white">
+ Link expires {{ fromNowTime }}
+ </p>
+
+ <label for="invite-link" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">
+ Invite link
+ </label>
+ <input
+ :value="inviteLink"
+ type="url"
+ id="invite-link"
+ class="input"
+ readonly
+ >
+
+ <div class="mt-4 ml-auto w-fit">
+ <button @click="copy(inviteLink)" type="submit" :disabled="copied" class="btn blue">
+ {{ copied ? 'Copied!' : 'Copy' }}
+ </button>
+ </div>
+
+ </div>
+
+ <form v-else @submit.prevent="onSubmit">
+
+ <fieldset class="mb-4">
+ <label for="user-email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Email address</label>
+ <input v-model="v$.email.$model" type="email" id="user-email" class="input"
+ :class="{ 'error': v$.email.$error, 'dirty': v$.email.$dirty }"
+ placeholder="user@simplebookmark.com"
+ required
+ >
+ </fieldset>
+
+ <fieldset class="px-2">
+ <h4 class="font-bold text-gray-700 dark:text-white">Permissions</h4>
+ <div class="flex items-center mt-2 mb-4">
+ <input id="can-invite-input" type="checkbox" v-model="canInvite" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
+ <label for="can-invite-input" class="text-sm font-medium text-gray-900 ms-2 dark:text-gray-300">Can invite users</label>
+ </div>
+ </fieldset>
+
+ <div class="ml-auto w-fit">
+ <button type="submit" class="btn blue">
+ Create Link
+ </button>
+ </div>
+ </form>
+ </div>
+ </template>
+ </Dialog>
+ </div>
+</template> \ No newline at end of file