mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 06:30:50 +01:00
switch to nuxt ui
This commit is contained in:
@ -1,8 +1,9 @@
|
||||
<template>
|
||||
<div class="items-center justify-center flex w-full relative min-h-full">
|
||||
<div class="absolute top-2 right-2">
|
||||
<UiDropdownLocale @select="setLocale" />
|
||||
<div class="items-center justify-center flex w-full h-full relative">
|
||||
<div class="absolute top-2 right-4">
|
||||
<UiDropdownLocale @select="onSelectLocale" />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col justify-center items-center gap-5 max-w-3xl">
|
||||
<UiLogoHaexhub class="bg-primary p-3 size-16 rounded-full shrink-0" />
|
||||
<span
|
||||
@ -32,16 +33,23 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="relative border-base-content/25 divide-base-content/25 flex w-full flex-col divide-y rounded-md border first:*:rounded-t-md last:*:rounded-b-md overflow-scroll"
|
||||
class="relative border-base-content/25 divide-base-content/25 flex w-full flex-col divide-y rounded-md border overflow-scroll"
|
||||
>
|
||||
<div
|
||||
v-for="vault in lastVaults"
|
||||
:key="vault.path"
|
||||
class="flex items-center justify-between group h-12 overflow-x-scroll"
|
||||
class="flex items-center justify-between group overflow-x-scroll"
|
||||
>
|
||||
<button
|
||||
class="link link-accent flex items-center no-underline justify-between text-nowrap text-sm md:text-base shrink w-full py-2 px-4"
|
||||
@click=";((passwordPromptOpen = true), (vaultPath = vault.path))"
|
||||
<UButton
|
||||
variant="ghost"
|
||||
color="neutral"
|
||||
class="flex items-center no-underline justify-between text-nowrap text-sm md:text-base shrink w-full px-3"
|
||||
@click="
|
||||
() => {
|
||||
passwordPromptOpen = true
|
||||
vaultPath = vault.path
|
||||
}
|
||||
"
|
||||
>
|
||||
<span class="block md:hidden">
|
||||
{{ vault.name }}
|
||||
@ -49,15 +57,17 @@
|
||||
<span class="hidden md:block">
|
||||
{{ vault.path }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="absolute right-2 btn btn-square btn-error btn-xs hidden group-hover:flex min-w-6"
|
||||
</UButton>
|
||||
<UButton
|
||||
color="error"
|
||||
square
|
||||
class="absolute right-2 hidden group-hover:flex min-w-6"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:trash-can-outline"
|
||||
@click="removeVaultAsync(vault.path)"
|
||||
/>
|
||||
</button>
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -65,9 +75,12 @@
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<h4>{{ t('sponsors') }}</h4>
|
||||
<div>
|
||||
<button @click="openUrl('https://itemis.com')">
|
||||
<UButton
|
||||
variant="link"
|
||||
@click="openUrl('https://itemis.com')"
|
||||
>
|
||||
<UiLogoItemis class="text-[#00457C]" />
|
||||
</button>
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -75,9 +88,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { UiLogoHaexhub } from '#components'
|
||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||
|
||||
import type { Locale } from 'vue-i18n'
|
||||
definePageMeta({
|
||||
name: 'vaultOpen',
|
||||
})
|
||||
@ -91,19 +103,20 @@ const { syncLastVaultsAsync, removeVaultAsync } = useLastVaultStore()
|
||||
const { lastVaults } = storeToRefs(useLastVaultStore())
|
||||
|
||||
await syncLastVaultsAsync()
|
||||
|
||||
const onSelectLocale = async (locale: Locale) => {
|
||||
await setLocale(locale)
|
||||
}
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"welcome": "Viel Spass mit",
|
||||
"lastUsed": "Zuletzt verwendete Vaults",
|
||||
"sponsors": "Supported by"
|
||||
},
|
||||
"en": {
|
||||
"welcome": "Have fun with",
|
||||
"lastUsed": "Last used Vaults",
|
||||
"sponsors": "Supported by"
|
||||
}
|
||||
}
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
welcome: 'Viel Spass mit'
|
||||
lastUsed: 'Zuletzt verwendete Vaults'
|
||||
sponsors: 'Supported by'
|
||||
|
||||
en:
|
||||
welcome: 'Have fun with'
|
||||
lastUsed: 'Last used Vaults'
|
||||
sponsors: 'Supported by'
|
||||
</i18n>
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
<template>
|
||||
<UiSidebarTest />
|
||||
<div>test</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
name: 'test',
|
||||
});
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -13,18 +13,20 @@
|
||||
confirm-icon="mdi:content-save-outline"
|
||||
v-model:open="showNewDeviceDialog"
|
||||
>
|
||||
<div class="flex flex-col gap-4">
|
||||
<p>{{ t('newDevice.intro') }}</p>
|
||||
<p>
|
||||
{{ t('newDevice.setName') }}
|
||||
</p>
|
||||
{{ deviceId }}
|
||||
<UiInput
|
||||
v-model="newDeviceName"
|
||||
:label="t('newDevice.label')"
|
||||
:rules="vaultDeviceNameSchema"
|
||||
/>
|
||||
</div>
|
||||
<template #body>
|
||||
<div class="flex flex-col gap-4">
|
||||
<p>{{ t('newDevice.intro') }}</p>
|
||||
<p>
|
||||
{{ t('newDevice.setName') }}
|
||||
</p>
|
||||
{{ deviceId }}
|
||||
<UiInput
|
||||
v-model="newDeviceName"
|
||||
:label="t('newDevice.label')"
|
||||
:rules="vaultDeviceNameSchema"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</UiDialogConfirm>
|
||||
</div>
|
||||
</div>
|
||||
@ -61,7 +63,7 @@ onMounted(async () => {
|
||||
}
|
||||
})
|
||||
|
||||
const { add } = useSnackbar()
|
||||
const { add } = useToast()
|
||||
const onSetDeviceNameAsync = async () => {
|
||||
try {
|
||||
const check = vaultDeviceNameSchema.safeParse(newDeviceName.value)
|
||||
@ -72,9 +74,9 @@ const onSetDeviceNameAsync = async () => {
|
||||
|
||||
await addDeviceNameAsync({ name: newDeviceName.value })
|
||||
showNewDeviceDialog.value = false
|
||||
add({ type: 'success', text: t('newDevice.success') })
|
||||
add({ color: 'success', description: t('newDevice.success') })
|
||||
} catch (error) {
|
||||
add({ type: 'error', text: t('newDevice.error') })
|
||||
add({ color: 'error', description: t('newDevice.error') })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
<template>
|
||||
<div class="w-full h-full overflow-scroll">
|
||||
<div>
|
||||
{{ iframeIndex }}
|
||||
</div>
|
||||
<iframe
|
||||
v-if="iframeIndex"
|
||||
ref="iFrameRef"
|
||||
class="w-full h-full"
|
||||
:src="iframeIndex"
|
||||
sandbox="allow-scripts allow-same-origin"
|
||||
allow="autoplay; speaker-selection; encrypted-media;"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
name: 'haexExtension',
|
||||
})
|
||||
|
||||
const { extensionEntry: iframeSrc } = storeToRefs(useExtensionsStore())
|
||||
|
||||
const iframeIndex = computed(() =>
|
||||
iframeSrc.value ? `${iframeSrc.value}/index.html` : '',
|
||||
)
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
loading: Erweiterung wird geladen
|
||||
en:
|
||||
loading: Extension is loading
|
||||
</i18n>
|
||||
@ -1,251 +0,0 @@
|
||||
<template>
|
||||
<div class="flex flex-col p-4 relative h-full">
|
||||
<div
|
||||
v-if="extensionStore.availableExtensions.length"
|
||||
class="flex"
|
||||
>
|
||||
<UiButton
|
||||
class="fixed top-20 right-4 btn-square btn-primary"
|
||||
@click="prepareInstallExtensionAsyn"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:plus"
|
||||
size="1.5em"
|
||||
/>
|
||||
</UiButton>
|
||||
|
||||
<HaexExtensionCard
|
||||
v-for="_extension in extensionStore.availableExtensions"
|
||||
v-bind="_extension"
|
||||
:key="_extension.id"
|
||||
@remove="onShowRemoveDialog(_extension)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="h-full w-full"
|
||||
>
|
||||
<Icon
|
||||
name="my-icon:extensions-overview"
|
||||
class="size-full md:size-2/3 md:translate-x-1/5 md:translate-y-1/3"
|
||||
/>
|
||||
<div class="fixed top-30 right-10">
|
||||
<UiTooltip :tooltip="t('extension.add')">
|
||||
<UiButton
|
||||
class="btn-square btn-primary btn-xl btn-gradient rotate-45"
|
||||
@click="prepareInstallExtensionAsyn"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:plus"
|
||||
size="1.5em"
|
||||
class="rotate-45"
|
||||
/>
|
||||
</UiButton>
|
||||
</UiTooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<HaexExtensionDialogReinstall
|
||||
v-model:open="openOverwriteDialog"
|
||||
:manifest="extension.manifest"
|
||||
@confirm="addExtensionAsync"
|
||||
/>
|
||||
|
||||
<HaexExtensionDialogInstall
|
||||
v-model:open="showConfirmation"
|
||||
:manifest="extension.manifest"
|
||||
@confirm="addExtensionAsync"
|
||||
/>
|
||||
|
||||
<HaexExtensionDialogRemove
|
||||
v-model:open="showRemoveDialog"
|
||||
:extension="extensionToBeRemoved"
|
||||
@confirm="removeExtensionAsync"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { join } from '@tauri-apps/api/path'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { readTextFile } from '@tauri-apps/plugin-fs'
|
||||
import type {
|
||||
IHaexHubExtension,
|
||||
IHaexHubExtensionManifest,
|
||||
} from '~/types/haexhub'
|
||||
|
||||
definePageMeta({
|
||||
name: 'extensionOverview',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const extensionStore = useExtensionsStore()
|
||||
|
||||
const showConfirmation = ref(false)
|
||||
const openOverwriteDialog = ref(false)
|
||||
|
||||
const extension = reactive<{
|
||||
manifest: IHaexHubExtensionManifest | null | undefined
|
||||
path: string | null
|
||||
}>({
|
||||
manifest: null,
|
||||
path: '',
|
||||
})
|
||||
|
||||
const loadExtensionManifestAsync = async () => {
|
||||
try {
|
||||
extension.path = await open({ directory: true, recursive: true })
|
||||
if (!extension.path) return
|
||||
|
||||
const manifestFile = JSON.parse(
|
||||
await readTextFile(await join(extension.path, 'manifest.json')),
|
||||
)
|
||||
|
||||
if (!extensionStore.checkManifest(manifestFile))
|
||||
throw new Error(`Manifest fehlerhaft ${JSON.stringify(manifestFile)}`)
|
||||
|
||||
return manifestFile
|
||||
} catch (error) {
|
||||
console.error('Fehler loadExtensionManifestAsync:', error)
|
||||
add({ type: 'error', text: JSON.stringify(error) })
|
||||
await addNotificationAsync({ text: JSON.stringify(error), type: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
const { add } = useSnackbar()
|
||||
const { addNotificationAsync } = useNotificationStore()
|
||||
|
||||
const prepareInstallExtensionAsyn = async () => {
|
||||
try {
|
||||
const manifest = await loadExtensionManifestAsync()
|
||||
if (!manifest) throw new Error('No valid Manifest found')
|
||||
|
||||
extension.manifest = manifest
|
||||
|
||||
const isAlreadyInstalled = await extensionStore.isExtensionInstalledAsync({
|
||||
id: manifest.id,
|
||||
version: manifest.version,
|
||||
})
|
||||
if (isAlreadyInstalled) {
|
||||
openOverwriteDialog.value = true
|
||||
} else {
|
||||
await addExtensionAsync()
|
||||
}
|
||||
} catch (error) {
|
||||
add({ type: 'error', text: JSON.stringify(error) })
|
||||
await addNotificationAsync({ text: JSON.stringify(error), type: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
const addExtensionAsync = async () => {
|
||||
try {
|
||||
await extensionStore.installAsync(extension.path)
|
||||
await extensionStore.loadExtensionsAsync()
|
||||
|
||||
add({
|
||||
type: 'success',
|
||||
title: t('extension.success.title', {
|
||||
extension: extension.manifest?.name,
|
||||
}),
|
||||
text: t('extension.success.text'),
|
||||
})
|
||||
await addNotificationAsync({
|
||||
text: t('extension.success.text'),
|
||||
type: 'success',
|
||||
title: t('extension.success.title', {
|
||||
extension: extension.manifest?.name,
|
||||
}),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Fehler addExtensionAsync:', error)
|
||||
add({ type: 'error', text: JSON.stringify(error) })
|
||||
await addNotificationAsync({ text: JSON.stringify(error), type: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
const showRemoveDialog = ref(false)
|
||||
const extensionToBeRemoved = ref<IHaexHubExtension>()
|
||||
|
||||
const onShowRemoveDialog = (extension: IHaexHubExtension) => {
|
||||
extensionToBeRemoved.value = extension
|
||||
showRemoveDialog.value = true
|
||||
}
|
||||
|
||||
const removeExtensionAsync = async () => {
|
||||
if (!extensionToBeRemoved.value?.id || !extensionToBeRemoved.value?.version) {
|
||||
add({ type: 'error', text: 'Erweiterung kann nicht gelöscht werden' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await extensionStore.removeExtensionAsync(
|
||||
extensionToBeRemoved.value.id,
|
||||
extensionToBeRemoved.value.version,
|
||||
)
|
||||
await extensionStore.loadExtensionsAsync()
|
||||
add({
|
||||
type: 'success',
|
||||
title: t('extension.remove.success.title', {
|
||||
extensionName: extensionToBeRemoved.value.name,
|
||||
}),
|
||||
text: t('extension.remove.success.text', {
|
||||
extensionName: extensionToBeRemoved.value.name,
|
||||
}),
|
||||
})
|
||||
await addNotificationAsync({
|
||||
text: t('extension.remove.success.text', {
|
||||
extensionName: extensionToBeRemoved.value.name,
|
||||
}),
|
||||
type: 'success',
|
||||
title: t('extension.remove.success.title', {
|
||||
extensionName: extensionToBeRemoved.value.name,
|
||||
}),
|
||||
})
|
||||
} catch (error) {
|
||||
add({
|
||||
type: 'error',
|
||||
title: t('extension.remove.error.title'),
|
||||
text: t('extension.remove.error.text', { error: JSON.stringify(error) }),
|
||||
})
|
||||
await addNotificationAsync({
|
||||
type: 'error',
|
||||
title: t('extension.remove.error.title'),
|
||||
text: t('extension.remove.error.text', { error: JSON.stringify(error) }),
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
title: 'Erweiterung installieren'
|
||||
extension:
|
||||
remove:
|
||||
success:
|
||||
text: 'Erweiterung {extensionName} wurde erfolgreich entfernt'
|
||||
title: '{extensionName} entfernt'
|
||||
error:
|
||||
text: "Erweiterung {extensionName} konnte nicht entfernt werden. \n {error}"
|
||||
title: 'Fehler beim Entfernen von {extensionName}'
|
||||
|
||||
add: 'Erweiterung hinzufügen'
|
||||
success:
|
||||
title: '{extension} hinzugefügt'
|
||||
text: 'Die Erweiterung wurde erfolgreich hinzugefügt'
|
||||
en:
|
||||
title: 'Install extension'
|
||||
extension:
|
||||
remove:
|
||||
success:
|
||||
text: 'Extension {extensionName} was removed'
|
||||
title: '{extensionName} removed'
|
||||
error:
|
||||
text: "Extension {extensionName} couldn't be removed. \n {error}"
|
||||
title: 'Exception during uninstall {extensionName}'
|
||||
|
||||
add: 'Add Extension'
|
||||
success:
|
||||
title: '{extension} added'
|
||||
text: 'Extensions was added successfully'
|
||||
</i18n>
|
||||
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="h-full text-base-content flex bg-base-200">
|
||||
<HaexExtensionCard
|
||||
v-for="extension in extensionStore.availableExtensions"
|
||||
v-bind="extension"
|
||||
:key="extension.id"
|
||||
/>
|
||||
<UiButton @click="requesty()">Storage Request</UiButton>
|
||||
res: {{ res }}
|
||||
<div class="text-base-content flex flex-col">
|
||||
<div class="h-screen bg-amber-300">
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
</div>
|
||||
<div class="h-screen bg-teal-300">
|
||||
abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
availableThemes:{{ uiStore.availableThemes }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -15,17 +15,5 @@ definePageMeta({
|
||||
name: 'vaultOverview',
|
||||
})
|
||||
|
||||
const storage = useAndroidStorage()
|
||||
const extensionStore = useExtensionsStore()
|
||||
|
||||
const res = ref()
|
||||
|
||||
const requesty = async () => {
|
||||
try {
|
||||
res.value = await storage.requestStoragePermission()
|
||||
res.value += ' wat the fuk'
|
||||
} catch (error) {
|
||||
res.value = error
|
||||
}
|
||||
}
|
||||
const uiStore = useUiStore()
|
||||
</script>
|
||||
|
||||
@ -1,148 +0,0 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<div class="p-6">
|
||||
<UiButton
|
||||
class="btn-error"
|
||||
@click="onDeleteNotificationsAsync"
|
||||
>
|
||||
{{ t('delete') }}
|
||||
</UiButton>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<input
|
||||
v-model="selectAll"
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-primary checkbox-sm"
|
||||
aria-label="notification"
|
||||
/>
|
||||
</th>
|
||||
<th>{{ t('title') }}</th>
|
||||
<th>{{ t('text') }}</th>
|
||||
<th>{{ t('date') }}</th>
|
||||
<th>{{ t('type') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="notification in notifications"
|
||||
:key="notification.id"
|
||||
>
|
||||
<td>
|
||||
<label>
|
||||
<input
|
||||
v-model="selectedNotificationIds"
|
||||
:name="notification.id"
|
||||
:value="notification.id"
|
||||
aria-label="notification"
|
||||
class="checkbox checkbox-primary checkbox-sm"
|
||||
type="checkbox"
|
||||
/>
|
||||
</label>
|
||||
</td>
|
||||
<td>{{ notification.title }}</td>
|
||||
<td>{{ notification.text }}</td>
|
||||
<td>
|
||||
{{
|
||||
notification.date
|
||||
? new Date(notification.date).toLocaleDateString(locale, {
|
||||
dateStyle: 'short',
|
||||
})
|
||||
: ''
|
||||
}}
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
class="badge badge-soft text-xs"
|
||||
:class="badgeClass[notification.type]"
|
||||
>
|
||||
{{ t(`types.${notification.type}`) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SelectHaexNotifications } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'notifications',
|
||||
})
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
const notifications = ref<SelectHaexNotifications[]>([])
|
||||
|
||||
const { deleteNotificationsAsync, syncNotificationsAsync } =
|
||||
useNotificationStore()
|
||||
|
||||
onMounted(async () => {
|
||||
await syncNotificationsAsync()
|
||||
})
|
||||
|
||||
const selectedNotificationIds = ref<string[]>([])
|
||||
const selectAll = computed({
|
||||
get() {
|
||||
return (
|
||||
notifications.value.length > 0 &&
|
||||
notifications.value.length === selectedNotificationIds.value.length
|
||||
)
|
||||
},
|
||||
set(value: boolean) {
|
||||
selectedNotificationIds.value = value
|
||||
? [...notifications.value.map((notification) => notification.id)]
|
||||
: []
|
||||
},
|
||||
})
|
||||
|
||||
const { add } = useSnackbar()
|
||||
|
||||
const onDeleteNotificationsAsync = async () => {
|
||||
try {
|
||||
console.log('onDeleteNotificationsAsync', selectedNotificationIds.value)
|
||||
await deleteNotificationsAsync(selectedNotificationIds.value)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
add({ type: 'error', text: JSON.stringify(error) })
|
||||
}
|
||||
}
|
||||
|
||||
const badgeClass: Record<SelectHaexNotifications['type'], string> = {
|
||||
error: 'badge-error',
|
||||
info: 'badge-info',
|
||||
success: 'badge-success',
|
||||
warning: 'badge-warning',
|
||||
log: 'badge-accent',
|
||||
}
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
title: Titel
|
||||
text: Text
|
||||
type: Typ
|
||||
date: Datum
|
||||
delete: Benachrichtigungen löschen
|
||||
types:
|
||||
error: Fehler
|
||||
info: Info
|
||||
success: Erfolg
|
||||
warning: Warnung
|
||||
|
||||
en:
|
||||
title: Title
|
||||
text: Text
|
||||
type: Type
|
||||
date: Date
|
||||
delete: Delete Notifications
|
||||
types:
|
||||
error: Error
|
||||
info: Info
|
||||
success: Success
|
||||
warning: Warning
|
||||
</i18n>
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="h-full overflow-auto p-2 relative">
|
||||
<div class="flex-1 p-2 relative h-full">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
{{ currentGroupId }}
|
||||
<HaexPassGroup
|
||||
v-model="group"
|
||||
mode="create"
|
||||
@ -9,20 +8,19 @@
|
||||
/>
|
||||
|
||||
<HaexPassMenuBottom
|
||||
@close="onClose"
|
||||
@save="createAsync"
|
||||
show-close-button
|
||||
show-save-button
|
||||
:has-changes
|
||||
>
|
||||
</HaexPassMenuBottom>
|
||||
@close="onClose"
|
||||
@save="createAsync"
|
||||
/>
|
||||
|
||||
<HaexPassDialogUnsavedChanges
|
||||
v-model:ignore-changes="ignoreChanges"
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
:has-changes
|
||||
@abort="showUnsavedChangesDialog = false"
|
||||
@confirm="onConfirmIgnoreChanges"
|
||||
v-model:ignore-changes="ignoreChanges"
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -45,6 +43,7 @@ const group = ref<SelectHaexPasswordsGroups>({
|
||||
parentId: currentGroupId.value || null,
|
||||
createdAt: null,
|
||||
updateAt: null,
|
||||
haex_tombstone: null,
|
||||
})
|
||||
|
||||
const errors = ref({
|
||||
|
||||
@ -1,43 +1,41 @@
|
||||
<template>
|
||||
<div>
|
||||
<HaexPassGroup
|
||||
:read_only
|
||||
v-model="group"
|
||||
:read-only
|
||||
mode="edit"
|
||||
@close="onClose"
|
||||
@submit="onSaveAsync"
|
||||
mode="edit"
|
||||
v-model="group"
|
||||
/>
|
||||
|
||||
<HaexPassMenuBottom
|
||||
:show-edit-button="read_only && !hasChanges"
|
||||
:show-readonly-button="!read_only && !hasChanges"
|
||||
:show-edit-button="readOnly && !hasChanges"
|
||||
:show-readonly-button="!readOnly && !hasChanges"
|
||||
:show-save-button="hasChanges"
|
||||
:has-changes
|
||||
@close="onClose()"
|
||||
@delete="showConfirmDeleteDialog = true"
|
||||
@edit="read_only = false"
|
||||
@readonly="read_only = true"
|
||||
@save="onSaveAsync"
|
||||
show-close-button
|
||||
show-delete-button
|
||||
>
|
||||
</HaexPassMenuBottom>
|
||||
@close="onClose()"
|
||||
@delete="showConfirmDeleteDialog = true"
|
||||
@edit="readOnly = false"
|
||||
@readonly="readOnly = true"
|
||||
@save="onSaveAsync"
|
||||
/>
|
||||
|
||||
<HaexPassDialogDeleteItem
|
||||
v-model:open="showConfirmDeleteDialog"
|
||||
@abort="showConfirmDeleteDialog = false"
|
||||
@confirm="onDeleteAsync"
|
||||
:item-name="group.name"
|
||||
:final="inTrashGroup"
|
||||
>
|
||||
</HaexPassDialogDeleteItem>
|
||||
@abort="showConfirmDeleteDialog = false"
|
||||
@confirm="onDeleteAsync"
|
||||
/>
|
||||
|
||||
<HaexPassDialogUnsavedChanges
|
||||
:has-changes="hasChanges"
|
||||
v-model:ignore-changes="ignoreChanges"
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
:has-changes="hasChanges"
|
||||
@abort="showUnsavedChangesDialog = false"
|
||||
@confirm="onConfirmIgnoreChanges"
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -59,47 +57,47 @@ const group = ref<SelectHaexPasswordsGroups>({
|
||||
description: null,
|
||||
icon: null,
|
||||
id: '',
|
||||
name: null,
|
||||
name: '',
|
||||
order: null,
|
||||
parentId: null,
|
||||
updateAt: null,
|
||||
haex_tombstone: null,
|
||||
})
|
||||
|
||||
const original = ref<string>('')
|
||||
const ignoreChanges = ref(false)
|
||||
|
||||
const { readGroupAsync } = usePasswordGroupStore()
|
||||
watch(
|
||||
currentGroupId,
|
||||
async () => {
|
||||
if (!currentGroupId.value) return
|
||||
ignoreChanges.value = false
|
||||
try {
|
||||
const foundGroup = await readGroupAsync(currentGroupId.value)
|
||||
if (foundGroup) {
|
||||
original.value = JSON.parse(JSON.stringify(foundGroup))
|
||||
group.value = foundGroup
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
watchImmediate(currentGroupId, async () => {
|
||||
if (!currentGroupId.value) return
|
||||
ignoreChanges.value = false
|
||||
try {
|
||||
const foundGroup = await readGroupAsync(currentGroupId.value)
|
||||
if (foundGroup) {
|
||||
original.value = JSON.parse(JSON.stringify(foundGroup))
|
||||
group.value = foundGroup
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
})
|
||||
|
||||
const read_only = ref(false)
|
||||
|
||||
const { areItemsEqual } = usePasswordGroup()
|
||||
const hasChanges = computed(() => !!!areItemsEqual(group.value, original.value))
|
||||
const hasChanges = computed(() => {
|
||||
const current = JSON.stringify(group.value)
|
||||
const origin = JSON.stringify(original.value)
|
||||
console.log('hasChanges', current, origin)
|
||||
return !(current === origin)
|
||||
})
|
||||
|
||||
const readOnly = ref(false)
|
||||
const onClose = () => {
|
||||
if (showConfirmDeleteDialog.value || showUnsavedChangesDialog.value) return
|
||||
|
||||
read_only.value = true
|
||||
readOnly.value = true
|
||||
useRouter().back()
|
||||
}
|
||||
|
||||
const { add } = useSnackbar()
|
||||
const { add } = useToast()
|
||||
|
||||
const { updateAsync, syncGroupItemsAsync, deleteGroupAsync } =
|
||||
usePasswordGroupStore()
|
||||
@ -111,21 +109,21 @@ const onSaveAsync = async () => {
|
||||
ignoreChanges.value = true
|
||||
await updateAsync(group.value)
|
||||
await syncGroupItemsAsync()
|
||||
add({ type: 'success', text: t('change.success') })
|
||||
add({ color: 'success', description: t('change.success') })
|
||||
onClose()
|
||||
} catch (error) {
|
||||
add({ type: 'error', text: t('change.error') })
|
||||
add({ color: 'error', description: t('change.error') })
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
const showConfirmDeleteDialog = ref(false)
|
||||
const showUnsavedChangesDialog = ref(false)
|
||||
const onConfirmIgnoreChanges = () => {
|
||||
showUnsavedChangesDialog.value = false
|
||||
onClose()
|
||||
}
|
||||
|
||||
const showConfirmDeleteDialog = ref(false)
|
||||
const onDeleteAsync = async () => {
|
||||
try {
|
||||
const parentId = group.value.parentId
|
||||
|
||||
@ -1,67 +1,72 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<div class="min-h-full flex flex-col">
|
||||
<div class="flex-1 h-full">
|
||||
<div class="h-full flex flex-col">
|
||||
<HaexPassGroupBreadcrumbs
|
||||
:items="breadCrumbs"
|
||||
class="px-2 sticky -top-2 z-10 bg-base-200"
|
||||
v-show="breadCrumbs.length"
|
||||
:items="breadCrumbs"
|
||||
class="px-2 sticky -top-2 z-10"
|
||||
/>
|
||||
<div class="flex-1 py-1 flex">
|
||||
<HaexPassMobileMenu
|
||||
:menu-items="groupItems"
|
||||
ref="listRef"
|
||||
v-model:selected-items="selectedItems"
|
||||
:menu-items="groupItems"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="fixed bottom-4 flex justify-between transition-all pointer-events-none right-0 sm:items-center items-end px-8"
|
||||
:class="[isVisible ? 'left-15 ' : 'left-0']"
|
||||
class="fixed bottom-4 flex justify-between transition-all w-full sm:items-center items-end px-8"
|
||||
>
|
||||
<div class="w-full"></div>
|
||||
<UiButtonAction
|
||||
v-if="!inTrashGroup"
|
||||
:menu
|
||||
/>
|
||||
<div class="w-full" />
|
||||
|
||||
<UDropdownMenu
|
||||
v-model:open="open"
|
||||
:items="menu"
|
||||
>
|
||||
<UButton
|
||||
icon="mdi:plus"
|
||||
:ui="{
|
||||
base: 'rotate-45 ',
|
||||
leadingIcon: [open ? 'rotate-0' : 'rotate-45', 'transition-all'],
|
||||
}"
|
||||
size="xl"
|
||||
/>
|
||||
</UDropdownMenu>
|
||||
|
||||
<div
|
||||
class="flex flex-col sm:flex-row gap-4 w-full justify-end items-end"
|
||||
>
|
||||
<UiButton
|
||||
v-show="selectedItems.size === 1"
|
||||
class="btn-square btn-accent"
|
||||
@click="onEditAsync"
|
||||
color="secondary"
|
||||
icon="mdi:pencil"
|
||||
:tooltip="t('edit')"
|
||||
>
|
||||
<Icon name="mdi:pencil" />
|
||||
</UiButton>
|
||||
@click="onEditAsync"
|
||||
/>
|
||||
|
||||
<UiButton
|
||||
class="btn-square btn-accent"
|
||||
v-show="selectedItems.size"
|
||||
@click="onCut"
|
||||
color="secondary"
|
||||
:tooltip="t('cut')"
|
||||
>
|
||||
<Icon name="mdi:scissors" />
|
||||
</UiButton>
|
||||
icon="mdi:scissors"
|
||||
@click="onCut"
|
||||
/>
|
||||
|
||||
<UiButton
|
||||
class="btn-square btn-accent"
|
||||
v-show="selectedGroupItems?.length"
|
||||
@click="onPasteAsync"
|
||||
color="secondary"
|
||||
icon="proicons:clipboard-paste"
|
||||
:tooltip="t('paste')"
|
||||
>
|
||||
<Icon name="proicons:clipboard-paste" />
|
||||
</UiButton>
|
||||
@click="onPasteAsync"
|
||||
/>
|
||||
|
||||
<UiButton
|
||||
v-show="selectedItems.size"
|
||||
class="btn-square btn-accent"
|
||||
@click="onDeleteAsync"
|
||||
color="secondary"
|
||||
icon="mdi:trash-outline"
|
||||
:tooltip="t('delete')"
|
||||
>
|
||||
<Icon name="mdi:trash" />
|
||||
</UiButton>
|
||||
@click="onDeleteAsync"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -70,15 +75,18 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { IPasswordMenuItem } from '~/components/haex/pass/mobile/menu/types'
|
||||
import { useMagicKeys } from '@vueuse/core'
|
||||
//import { useMagicKeys, whenever } from '@vueuse/core'
|
||||
import Fuse from 'fuse.js'
|
||||
|
||||
definePageMeta({
|
||||
name: 'passwordGroupItems',
|
||||
})
|
||||
|
||||
const open = ref(false)
|
||||
|
||||
const { t } = useI18n()
|
||||
const { add } = useSnackbar()
|
||||
|
||||
const { add } = useToast()
|
||||
|
||||
const selectedItems = ref<Set<IPasswordMenuItem>>(new Set())
|
||||
const { menu } = storeToRefs(usePasswordsActionMenuStore())
|
||||
@ -88,7 +96,9 @@ const { syncGroupItemsAsync } = usePasswordGroupStore()
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await Promise.allSettled([syncItemsAsync(), syncGroupItemsAsync()])
|
||||
} catch (error) {}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
})
|
||||
|
||||
const {
|
||||
@ -146,8 +156,6 @@ const groupItems = computed<IPasswordMenuItem[]>(() => {
|
||||
return menuItems
|
||||
})
|
||||
|
||||
const { isVisible } = storeToRefs(useSidebarStore())
|
||||
|
||||
const onEditAsync = async () => {
|
||||
const item = selectedItems.value.values().next().value
|
||||
|
||||
@ -200,7 +208,7 @@ const onPasteAsync = async () => {
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
selectedGroupItems.value = []
|
||||
add({ type: 'error', text: t('error.paste') })
|
||||
add({ color: 'error', description: t('error.paste') })
|
||||
}
|
||||
}
|
||||
onKeyStroke('v', async (event) => {
|
||||
@ -209,10 +217,12 @@ onKeyStroke('v', async (event) => {
|
||||
}
|
||||
})
|
||||
|
||||
const { escape } = useMagicKeys()
|
||||
watch(escape, () => {
|
||||
/* const { escape } = useMagicKeys()
|
||||
whenever(escape, () => {
|
||||
selectedItems.value.clear()
|
||||
})
|
||||
}) */
|
||||
|
||||
onKeyStroke('escape', () => selectedItems.value.clear())
|
||||
|
||||
onKeyStroke('a', (event) => {
|
||||
if (event.ctrlKey) {
|
||||
@ -237,10 +247,11 @@ const onDeleteAsync = async () => {
|
||||
selectedItems.value.clear()
|
||||
await syncGroupItemsAsync()
|
||||
}
|
||||
const keys = useMagicKeys()
|
||||
watch(keys.delete, async () => {
|
||||
/* const keys = useMagicKeys()
|
||||
whenever(keys, async () => {
|
||||
await onDeleteAsync()
|
||||
})
|
||||
}) */
|
||||
onKeyStroke('delete', () => onDeleteAsync())
|
||||
|
||||
const listRef = useTemplateRef<HTMLElement>('listRef')
|
||||
onClickOutside(listRef, () => setTimeout(() => selectedItems.value.clear(), 50))
|
||||
@ -252,6 +263,7 @@ de:
|
||||
paste: Einfügen
|
||||
delete: Löschen
|
||||
edit: Bearbeiten
|
||||
wtf: 'wtf'
|
||||
en:
|
||||
cut: Cut
|
||||
paste: Paste
|
||||
|
||||
@ -1,56 +1,53 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex flex-col">
|
||||
<!-- <div class="flex flex-col">
|
||||
<p>
|
||||
{{ item.originalDetails }}
|
||||
</p>
|
||||
{{ item.details }}
|
||||
</div>
|
||||
</div> -->
|
||||
<HaexPassItem
|
||||
:history="item.history"
|
||||
:read_only
|
||||
@close="onClose()"
|
||||
@submit="onUpdateAsync"
|
||||
v-model:details="item.details"
|
||||
v-model:key-values-add="item.keyValuesAdd"
|
||||
v-model:key-values-delete="item.keyValuesDelete"
|
||||
v-model:key-values="item.keyValues"
|
||||
:history="item.history"
|
||||
:read-only
|
||||
@close="onClose()"
|
||||
@submit="onUpdateAsync"
|
||||
/>
|
||||
|
||||
<HaexPassMenuBottom
|
||||
:has-changes
|
||||
:show-edit-button="read_only && !hasChanges"
|
||||
:show-readonly-button="!read_only && !hasChanges"
|
||||
:show-save-button="!read_only && hasChanges"
|
||||
@close="onClose"
|
||||
@delete="showConfirmDeleteDialog = true"
|
||||
@edit="read_only = false"
|
||||
@readonly="read_only = true"
|
||||
@save="onUpdateAsync"
|
||||
:show-edit-button="readOnly && !hasChanges"
|
||||
:show-readonly-button="!readOnly && !hasChanges"
|
||||
:show-save-button="!readOnly && hasChanges"
|
||||
show-close-button
|
||||
show-delete-button
|
||||
>
|
||||
</HaexPassMenuBottom>
|
||||
@close="onClose"
|
||||
@delete="showConfirmDeleteDialog = true"
|
||||
@edit="readOnly = false"
|
||||
@readonly="readOnly = true"
|
||||
@save="onUpdateAsync"
|
||||
/>
|
||||
|
||||
<HaexPassDialogDeleteItem
|
||||
v-model:open="showConfirmDeleteDialog"
|
||||
@abort="showConfirmDeleteDialog = false"
|
||||
@confirm="deleteItemAsync"
|
||||
>
|
||||
</HaexPassDialogDeleteItem>
|
||||
/>
|
||||
|
||||
<HaexPassDialogUnsavedChanges
|
||||
:has-changes="hasChanges"
|
||||
v-model:ignore-changes="ignoreChanges"
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
:has-changes="hasChanges"
|
||||
@abort="showUnsavedChangesDialog = false"
|
||||
@confirm="onConfirmIgnoreChanges"
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { usePasswordGroup } from '~/components/haex/pass/group/composables'
|
||||
import type {
|
||||
SelectHaexPasswordsItemDetails,
|
||||
SelectHaexPasswordsItemHistory,
|
||||
@ -61,13 +58,13 @@ definePageMeta({
|
||||
name: 'passwordItemEdit',
|
||||
})
|
||||
|
||||
defineProps({
|
||||
/* defineProps({
|
||||
icon: String,
|
||||
title: String,
|
||||
withCopyButton: Boolean,
|
||||
})
|
||||
}) */
|
||||
|
||||
const read_only = ref(true)
|
||||
const readOnly = ref(true)
|
||||
const showConfirmDeleteDialog = ref(false)
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -91,6 +88,7 @@ const item = reactive<{
|
||||
updateAt: null,
|
||||
url: null,
|
||||
username: null,
|
||||
haex_tombstone: null,
|
||||
},
|
||||
keyValues: [],
|
||||
history: [],
|
||||
@ -107,6 +105,7 @@ const item = reactive<{
|
||||
updateAt: null,
|
||||
url: null,
|
||||
username: null,
|
||||
haex_tombstone: null,
|
||||
},
|
||||
originalKeyValues: null,
|
||||
})
|
||||
@ -132,7 +131,7 @@ watch(
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const { add } = useSnackbar()
|
||||
const { add } = useToast()
|
||||
const { deleteAsync, updateAsync } = usePasswordItemStore()
|
||||
const { syncGroupItemsAsync } = usePasswordGroupStore()
|
||||
const { currentGroupId, inTrashGroup } = storeToRefs(usePasswordGroupStore())
|
||||
@ -147,12 +146,13 @@ const onUpdateAsync = async () => {
|
||||
keyValuesAdd: item.keyValuesAdd,
|
||||
keyValuesDelete: item.keyValuesDelete,
|
||||
})
|
||||
if (newId) add({ type: 'success', text: t('success.update') })
|
||||
if (newId) add({ color: 'success', description: t('success.update') })
|
||||
syncGroupItemsAsync()
|
||||
ignoreChanges.value = true
|
||||
onClose()
|
||||
} catch (error) {
|
||||
add({ type: 'error', text: t('error.update') })
|
||||
console.log(error)
|
||||
add({ color: 'error', description: t('error.update') })
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,7 +162,7 @@ const onClose = () => {
|
||||
if (hasChanges.value && !ignoreChanges.value)
|
||||
return (showUnsavedChangesDialog.value = true)
|
||||
|
||||
read_only.value = true
|
||||
readOnly.value = true
|
||||
useRouter().back()
|
||||
}
|
||||
|
||||
@ -170,27 +170,26 @@ const deleteItemAsync = async () => {
|
||||
try {
|
||||
await deleteAsync(item.details.id, inTrashGroup.value)
|
||||
showConfirmDeleteDialog.value = false
|
||||
add({ type: 'success', text: t('success.delete') })
|
||||
add({ color: 'success', description: t('success.delete') })
|
||||
await syncGroupItemsAsync()
|
||||
onClose()
|
||||
} catch (errro) {
|
||||
console.log(errro)
|
||||
add({
|
||||
type: 'error',
|
||||
text: t('error.delete'),
|
||||
color: 'error',
|
||||
description: t('error.delete'),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const { areItemsEqual } = usePasswordGroup()
|
||||
const hasChanges = computed(
|
||||
() =>
|
||||
!!(
|
||||
!areItemsEqual(item.originalDetails, item.details) ||
|
||||
!areItemsEqual(item.originalKeyValues, item.keyValues) ||
|
||||
item.keyValuesAdd.length ||
|
||||
item.keyValuesDelete.length
|
||||
),
|
||||
)
|
||||
const hasChanges = computed(() => {
|
||||
return !(
|
||||
JSON.stringify(item.originalDetails) === JSON.stringify(item.details) &&
|
||||
JSON.stringify(item.originalKeyValues) === JSON.stringify(item.keyValues) &&
|
||||
!item.keyValuesAdd.length &&
|
||||
!item.keyValuesDelete.length
|
||||
)
|
||||
})
|
||||
|
||||
const showUnsavedChangesDialog = ref(false)
|
||||
const onConfirmIgnoreChanges = () => {
|
||||
|
||||
@ -1,30 +1,28 @@
|
||||
<template>
|
||||
<div>
|
||||
{{ currentGroup?.id }} {{ currentGroupId }}
|
||||
<HaexPassItem
|
||||
v-model:details="item.details"
|
||||
v-model:key-values-add="item.keyValuesAdd"
|
||||
:default-icon="currentGroup?.icon"
|
||||
:history="item.history"
|
||||
@close="onClose"
|
||||
@submit="onCreateAsync"
|
||||
v-model:details="item.details"
|
||||
v-model:key-values-add="item.keyValuesAdd"
|
||||
/>
|
||||
|
||||
<HaexPassMenuBottom
|
||||
:has-changes
|
||||
:show-close-button="true"
|
||||
:show-save-button="true"
|
||||
@close="onClose"
|
||||
@save="onCreateAsync"
|
||||
show-close-button
|
||||
show-save-button
|
||||
>
|
||||
</HaexPassMenuBottom>
|
||||
/>
|
||||
|
||||
<HaexPassDialogUnsavedChanges
|
||||
:has-changes="hasChanges"
|
||||
v-model:ignore-changes="ignoreChanges"
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
:has-changes="hasChanges"
|
||||
@abort="showUnsavedChangesDialog = false"
|
||||
@confirm="onConfirmIgnoreChanges"
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -40,11 +38,11 @@ definePageMeta({
|
||||
name: 'passwordItemCreate',
|
||||
})
|
||||
|
||||
defineProps({
|
||||
icon: String,
|
||||
title: String,
|
||||
withCopyButton: Boolean,
|
||||
})
|
||||
defineProps<{
|
||||
icon: string
|
||||
title: string
|
||||
withCopyButton: boolean
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -56,9 +54,10 @@ const item = reactive<{
|
||||
originalKeyValuesAdd: []
|
||||
}>({
|
||||
details: {
|
||||
id: '',
|
||||
createdAt: null,
|
||||
haex_tombstone: null,
|
||||
icon: null,
|
||||
id: '',
|
||||
note: null,
|
||||
password: null,
|
||||
tags: null,
|
||||
@ -70,9 +69,10 @@ const item = reactive<{
|
||||
history: [],
|
||||
keyValuesAdd: [],
|
||||
originalDetails: {
|
||||
id: '',
|
||||
createdAt: null,
|
||||
haex_tombstone: null,
|
||||
icon: null,
|
||||
id: '',
|
||||
note: null,
|
||||
password: null,
|
||||
tags: null,
|
||||
@ -84,8 +84,8 @@ const item = reactive<{
|
||||
originalKeyValuesAdd: [],
|
||||
})
|
||||
|
||||
const { add } = useSnackbar()
|
||||
const { currentGroup, currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||
const { add } = useToast()
|
||||
const { currentGroup } = storeToRefs(usePasswordGroupStore())
|
||||
const { syncGroupItemsAsync } = usePasswordGroupStore()
|
||||
const { addAsync } = usePasswordItemStore()
|
||||
|
||||
@ -99,12 +99,13 @@ const onCreateAsync = async () => {
|
||||
|
||||
if (newId) {
|
||||
ignoreChanges.value = true
|
||||
add({ type: 'success', text: t('success') })
|
||||
add({ color: 'success', description: t('success') })
|
||||
await syncGroupItemsAsync()
|
||||
onClose()
|
||||
}
|
||||
} catch (error) {
|
||||
add({ type: 'error', text: t('error') })
|
||||
console.log(error)
|
||||
add({ color: 'error', description: t('error') })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,28 +13,16 @@
|
||||
<UiInput
|
||||
v-model="currentVaultName"
|
||||
:placeholder="t('vaultName.label')"
|
||||
>
|
||||
<template #append>
|
||||
<UiTooltip :tooltip="t('save')">
|
||||
<UiButton
|
||||
class="btn-primary"
|
||||
@click="onSetVaultNameAsync"
|
||||
>
|
||||
<Icon name="mdi:content-save-outline" />
|
||||
</UiButton>
|
||||
</UiTooltip>
|
||||
</template>
|
||||
</UiInput>
|
||||
@change="onSetVaultNameAsync"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="p-2">{{ t('notifications.label') }}</div>
|
||||
<div class="flex items-center">
|
||||
<div>
|
||||
<UiButton
|
||||
class="btn-primary"
|
||||
:label="t('notifications.requestPermission')"
|
||||
@click="requestNotificationPermissionAsync"
|
||||
>
|
||||
{{ t('notifications.requestPermission') }}
|
||||
</UiButton>
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="p-2">{{ t('deviceName.label') }}</div>
|
||||
@ -42,18 +30,8 @@
|
||||
<UiInput
|
||||
v-model="deviceName"
|
||||
:placeholder="t('deviceName.label')"
|
||||
>
|
||||
<template #append>
|
||||
<UiButton
|
||||
class="btn-primary"
|
||||
@click="onUpdateDeviceNameAsync"
|
||||
:tooltip="t('save')"
|
||||
:rules="vaultDeviceNameSchema"
|
||||
>
|
||||
<Icon name="mdi:content-save-outline" />
|
||||
</UiButton>
|
||||
</template>
|
||||
</UiInput>
|
||||
@change="onUpdateDeviceNameAsync"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -76,21 +54,23 @@ const onSelectLocaleAsync = async (locale: Locale) => {
|
||||
await setLocale(locale)
|
||||
}
|
||||
|
||||
const { currentTheme } = storeToRefs(useUiStore())
|
||||
const { currentThemeName } = storeToRefs(useUiStore())
|
||||
|
||||
const onSelectThemeAsync = async (theme: ITheme) => {
|
||||
await updateThemeAsync(theme.value)
|
||||
currentTheme.value = theme
|
||||
const onSelectThemeAsync = async (theme: string) => {
|
||||
currentThemeName.value = theme
|
||||
console.log('onSelectThemeAsync', currentThemeName.value)
|
||||
await updateThemeAsync(theme)
|
||||
}
|
||||
|
||||
const { add } = useSnackbar()
|
||||
const { add } = useToast()
|
||||
|
||||
const onSetVaultNameAsync = async () => {
|
||||
try {
|
||||
await updateVaultNameAsync(currentVaultName.value)
|
||||
add({ text: t('vaultName.update.success'), type: 'success' })
|
||||
add({ description: t('vaultName.update.success'), color: 'success' })
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
add({ text: t('vaultName.update.error'), type: 'error' })
|
||||
add({ description: t('vaultName.update.error'), color: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,14 +82,16 @@ const { updateDeviceNameAsync, readDeviceNameAsync } = useDeviceStore()
|
||||
onMounted(async () => {
|
||||
await readDeviceNameAsync()
|
||||
})
|
||||
|
||||
const onUpdateDeviceNameAsync = async () => {
|
||||
const check = vaultDeviceNameSchema.safeParse(deviceName.value)
|
||||
if (!check.success) return
|
||||
try {
|
||||
await updateDeviceNameAsync({ name: deviceName.value })
|
||||
add({ text: t('deviceName.update.success'), type: 'success' })
|
||||
add({ description: t('deviceName.update.success'), color: 'success' })
|
||||
} catch (error) {
|
||||
add({ text: t('deviceName.update.error'), type: 'error' })
|
||||
console.log(error)
|
||||
add({ description: t('deviceName.update.error'), color: 'error' })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user