aboutsummaryrefslogtreecommitdiff
path: root/front-end/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'front-end/src/components')
-rw-r--r--front-end/src/components/Bookmarks.vue1
-rw-r--r--front-end/src/components/Boomarks/AddOrUpdateForm.vue118
-rw-r--r--front-end/src/components/Settings.vue8
-rw-r--r--front-end/src/components/Settings/Bookmarks.vue84
-rw-r--r--front-end/src/components/Settings/PkiSettings.vue5
-rw-r--r--front-end/src/components/Settings/Registation.vue2
6 files changed, 150 insertions, 68 deletions
diff --git a/front-end/src/components/Bookmarks.vue b/front-end/src/components/Bookmarks.vue
index cc3cd6a..274b0b4 100644
--- a/front-end/src/components/Bookmarks.vue
+++ b/front-end/src/components/Bookmarks.vue
@@ -387,7 +387,6 @@ const upload = (() => {
<span class="sr-only">Search</span>
</button>
</form>
-
</div>
<div class="relative ml-3 md:ml-10">
diff --git a/front-end/src/components/Boomarks/AddOrUpdateForm.vue b/front-end/src/components/Boomarks/AddOrUpdateForm.vue
index a4a3f1d..0370e0c 100644
--- a/front-end/src/components/Boomarks/AddOrUpdateForm.vue
+++ b/front-end/src/components/Boomarks/AddOrUpdateForm.vue
@@ -1,6 +1,8 @@
<script setup lang="ts">
import { computed, toRefs } from 'vue';
-import { join, split } from 'lodash-es';
+import { isEmpty, join, split } from 'lodash-es';
+import { useStore } from '../../store';
+import { useWait } from '@vnuge/vnlib.browser';
const emit = defineEmits(['submit'])
const props = defineProps<{
@@ -15,54 +17,110 @@ const tags = computed({
set: (value:string) => v$.value.Tags.$model = split(value, ',')
})
+const { websiteLookup:lookup } = useStore()
+const { setWaiting, waiting } = useWait()
+
+const execLookup = async () => {
+ //url must be valid before searching
+ if(v$.value.Url.$invalid) return
+
+ setWaiting(true)
+
+ try{
+ const { title, description, keywords } = await lookup.execLookup(v$.value.Url.$model);
+
+ //Set the title and description
+ if(title){
+ v$.value.Name.$model = title;
+ v$.value.Name.$dirty = true;
+ }
+
+ if(description){
+ v$.value.Description.$model = description;
+ v$.value.Description.$dirty = true;
+ }
+
+ if(!isEmpty(keywords)){
+ v$.value.Tags.$model = keywords;
+ v$.value.Tags.$dirty = true;
+ }
+ }
+ catch(e){
+ //Mostly ignore errors
+ console.error(e)
+ }
+ finally{
+ setWaiting(false)
+ }
+}
+
+const showSearchButton = computed(() => lookup.isSupported && !isEmpty(v$.value.Url.$model))
+
</script>
<template>
- <form class="grid grid-cols-1 gap-4 p-4" @submit.prevent="emit('submit')">
+ <form id="bm-add-or-update-form" class="grid grid-cols-1 gap-4 p-4" @submit.prevent="emit('submit')">
<fieldset>
- <label for="url" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">URL</label>
- <input type="text" id="url" class="input" placeholder="https://www.example.com"
- v-model="v$.Url.$model"
- :class="{'dirty': v$.Url.$dirty, 'error': v$.Url.$invalid}"
- required
- >
+ <label for="url" class="flex justify-between mb-2 text-sm font-medium text-gray-900 dark:text-white">
+ URL
+ </label>
+ <div class="flex gap-2">
+ <input type="text" id="url" class="input" placeholder="https://www.example.com" v-model="v$.Url.$model"
+ :class="{'dirty': v$.Url.$dirty, 'error': v$.Url.$invalid}" required>
+
+ <div class="">
+ <button
+ type="button"
+ :disabled="!showSearchButton || waiting"
+ @click.self.prevent="execLookup"
+ id="search-btn"
+ class="btn blue search-btn"
+ >
+ <svg class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
+ viewBox="0 0 20 20">
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
+ d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
+ </svg>
+ </button>
+ </div>
+ </div>
</fieldset>
<fieldset>
<label for="name" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Title</label>
- <input type="text" id="Name" class="input" placeholder="Hello World"
- v-model="v$.Name.$model"
- :class="{'dirty': v$.Name.$dirty, 'error': v$.Name.$invalid}"
- required
- >
+ <input type="text" id="Name" class="input" placeholder="Hello World" v-model="v$.Name.$model"
+ :class="{'dirty': v$.Name.$dirty, 'error': v$.Name.$invalid}" required>
</fieldset>
- <fieldset>
+ <fieldset>
<label for="tags" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Tags</label>
- <input type="text" id="tags" class="input" placeholder="tag1,tag2,tag3"
- v-model="tags"
- :class="{'dirty': v$.Tags.$dirty, 'error': v$.Tags.$invalid}"
- >
+ <input type="text" id="tags" class="input" placeholder="tag1,tag2,tag3" v-model="tags"
+ :class="{'dirty': v$.Tags.$dirty, 'error': v$.Tags.$invalid}">
</fieldset>
<fieldset>
- <label for="description" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Description</label>
+ <label for="description"
+ class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Description</label>
<textarea type="text" id="description" rows="5" class="input" placeholder="This is a bookmark"
v-model="v$.Description.$model"
- :class="{'dirty': v$.Description.$dirty, 'error': v$.Description.$invalid}"
- />
+ :class="{'dirty': v$.Description.$dirty, 'error': v$.Description.$invalid}" />
</fieldset>
-
+
<div class="flex justify-end">
- <button type="submit" class="btn blue">
- Submit
+ <button id="save-button" type="submit" form="bm-add-or-update-form" class="btn blue">
+ Save
</button>
</div>
</form>
</template>
-<style scoped lang="scss">input.search {
- @apply ps-10 p-2.5 border block w-full text-sm rounded;
- @apply bg-gray-50 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 border-gray-300 text-gray-900 focus:ring-blue-500 focus:border-blue-500;
-}
+<style scoped lang="scss">
+
+#bm-add-or-update-form {
+ .search-btn{
-button.search {
- @apply p-2.5 ms-2 text-sm font-medium text-white bg-blue-700 rounded border border-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
+ @apply my-auto px-3 py-2.5;
+
+ &:disabled{
+ @apply bg-gray-600;
+ }
+ }
}
+
</style> \ No newline at end of file
diff --git a/front-end/src/components/Settings.vue b/front-end/src/components/Settings.vue
index 83d3f79..504f38a 100644
--- a/front-end/src/components/Settings.vue
+++ b/front-end/src/components/Settings.vue
@@ -19,7 +19,7 @@ const darkMode = useDark();
<h2 class="text-2xl font-bold">Settings</h2>
<div class="flex flex-col w-full max-w-3xl gap-10 mt-3">
- <div class="">
+ <div class="mb-6">
<h3 class="text-xl font-bold">
General
</h3>
@@ -41,7 +41,7 @@ const darkMode = useDark();
</div>
</div>
- <div class="">
+ <div class="mb-6">
<h3 class="text-xl font-bold">Boomarks</h3>
<div class="relative mt-4">
@@ -51,7 +51,7 @@ const darkMode = useDark();
<PasswordReset />
- <div class="">
+ <div class="mb-8">
<h3 class="text-xl font-bold">Multi Factor Auth</h3>
<div class="relative mt-4 py-2.5">
@@ -66,7 +66,7 @@ const darkMode = useDark();
<Oauth2Apps />
</div>
- <div v-if="store.registation.status?.can_invite" class="mb-10">
+ <div v-if="store.registation.status?.can_invite" class="mt-6 mb-10">
<Registation />
</div>
diff --git a/front-end/src/components/Settings/Bookmarks.vue b/front-end/src/components/Settings/Bookmarks.vue
index a4ab55a..aa4ed31 100644
--- a/front-end/src/components/Settings/Bookmarks.vue
+++ b/front-end/src/components/Settings/Bookmarks.vue
@@ -1,13 +1,14 @@
<script setup lang="ts">
import { apiCall, useWait } from '@vnuge/vnlib.browser';
import { useStore, type DownloadContentType, TabId } from '../../store';
-import { ref } from 'vue';
+import { computed, ref } from 'vue';
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
-const { bookmarks } = useStore();
+const { bookmarks, websiteLookup } = useStore();
const downloadAnchor = ref();
const { waiting } = useWait()
+const curlSupported = computed(() => websiteLookup.isSupported);
const downloadBookmarks = (contentType: DownloadContentType) => {
apiCall(async () => {
@@ -58,8 +59,10 @@ javascript: (function() {
</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 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>
@@ -72,47 +75,66 @@ javascript: (function() {
<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"/>
+ <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"
+ <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">
+ 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>
+ <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>
+ <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 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 class="mt-3">
+ <h4 class="mb-2 font-bold">Features</h4>
+ <p class="text-sm text-gray-500 dark:text-gray-400">
+ Some features for Simple-Bookmark use tools and applications that are already installed on
+ your server such as curl.
+ </p>
+ <div class="flex flex-row gap-2 mt-4">
+ <span class="w-3 h-3 my-auto rounded-full" :class="[curlSupported ? 'bg-green-500' : 'bg-amber-500']"></span>
+ <span class="my-auto font-bold">
+ curl
+ </span>
+ <span class="my-auto text-sm text-gray-500 ms-4 dark:text-gray-400">
+ Curl is used to fetch website details like title, description and tags.
+ {{ curlSupported ? '(supported)' : '(not supported)' }}
+ </span>
+ </div>
+ </div>
</div>
-
+
<a ref="downloadAnchor" class="hidden"></a>
</template> \ No newline at end of file
diff --git a/front-end/src/components/Settings/PkiSettings.vue b/front-end/src/components/Settings/PkiSettings.vue
index dfa4cad..885b2cb 100644
--- a/front-end/src/components/Settings/PkiSettings.vue
+++ b/front-end/src/components/Settings/PkiSettings.vue
@@ -32,6 +32,8 @@ const removeKey = async (key: PkiPublicKey) => {
title: 'Key Removed',
text: `${key.kid} has been successfully removed`
})
+
+ store.mfaRefreshMethods()
})
}
@@ -117,7 +119,8 @@ const onAddKey = async () => {
text: result
})
- hideAddKeyDialog()
+ hideAddKeyDialog();
+ store.mfaRefreshMethods();
})
}
diff --git a/front-end/src/components/Settings/Registation.vue b/front-end/src/components/Settings/Registation.vue
index a0f208e..d0dfaa7 100644
--- a/front-end/src/components/Settings/Registation.vue
+++ b/front-end/src/components/Settings/Registation.vue
@@ -58,7 +58,7 @@ const onCancel = () => {
<div class="">
<div class="flex flex-row justify-between w-full">
- <h3 class="text-xl font-bold">Registation</h3>
+ <h3 class="text-xl font-bold">Invite Links</h3>
<div class="flex flex-row justify-end">
<button class="btn blue" @click="toggleOpen(true)">Invite User</button>