mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 06:30:50 +01:00
mobile menu
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="items-center justify-center min-h-full flex w-full relative">
|
||||
<div class="items-center justify-center h-full flex w-full relative">
|
||||
<div class="fixed top-2 right-2">
|
||||
<UiDropdownLocale @select="setLocale" />
|
||||
</div>
|
||||
@ -78,7 +78,6 @@
|
||||
import { UiLogoHaexhub } from '#components'
|
||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||
|
||||
const refresh = () => location.reload()
|
||||
definePageMeta({
|
||||
name: 'vaultOpen',
|
||||
})
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full">
|
||||
<div class="h-full w-full">
|
||||
<NuxtLayout name="app">
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
|
||||
@ -109,10 +109,12 @@ const loadExtensionManifestAsync = async () => {
|
||||
} 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 {
|
||||
@ -132,6 +134,7 @@ const prepareInstallExtensionAsyn = async () => {
|
||||
}
|
||||
} catch (error) {
|
||||
add({ type: 'error', text: JSON.stringify(error) })
|
||||
await addNotificationAsync({ text: JSON.stringify(error), type: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,9 +150,17 @@ const addExtensionAsync = async () => {
|
||||
}),
|
||||
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' })
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,12 +193,26 @@ const removeExtensionAsync = async () => {
|
||||
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>
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
<template>
|
||||
<div class="flex">
|
||||
<HaexPassSidebar />
|
||||
<div>
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,9 +0,0 @@
|
||||
<template>
|
||||
<div>passwords</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
name: "haexpassOverview"
|
||||
})
|
||||
</script>
|
||||
148
src/pages/vault/[vaultId]/notifications.vue
Normal file
148
src/pages/vault/[vaultId]/notifications.vue
Normal file
@ -0,0 +1,148 @@
|
||||
<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>
|
||||
13
src/pages/vault/[vaultId]/passwords.vue
Normal file
13
src/pages/vault/[vaultId]/passwords.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div class="p-4">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
name: 'passwords',
|
||||
})
|
||||
|
||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||
</script>
|
||||
147
src/pages/vault/[vaultId]/passwords/[[groupId]]/create.vue
Normal file
147
src/pages/vault/[vaultId]/passwords/[[groupId]]/create.vue
Normal file
@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div>
|
||||
<VaultCard
|
||||
:title="t('title')"
|
||||
icon="mdi:folder-plus-outline"
|
||||
@close="onClose"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col gap-4 w-full p-4"
|
||||
@keyup.enter="onCreate"
|
||||
>
|
||||
<UiInput
|
||||
:check-input="check"
|
||||
:label="t('name.label')"
|
||||
:placeholder="t('name.label')"
|
||||
autofocus
|
||||
v-model:errors="errors.name"
|
||||
v-model="vaultGroup.name"
|
||||
/>
|
||||
|
||||
<UiInput
|
||||
v-model="vaultGroup.description"
|
||||
:check-input="check"
|
||||
:label="t('description.label')"
|
||||
:placeholder="t('description.label')"
|
||||
/>
|
||||
|
||||
<UiSelectColor v-model="vaultGroup.color" />
|
||||
|
||||
{{ vaultGroup.icon }}
|
||||
<UiSelectIcon v-model="vaultGroup.icon" />
|
||||
|
||||
<div class="flex flex-wrap justify-end gap-4">
|
||||
<button
|
||||
class="btn btn-error btn-outline flex-1 flex-nowrap"
|
||||
@click="onClose"
|
||||
type="button"
|
||||
>
|
||||
{{ t('abort') }}
|
||||
<Icon name="mdi:close" />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary flex-1 flex-nowrap"
|
||||
type="button"
|
||||
@click="onCreate"
|
||||
>
|
||||
{{ t('create') }}
|
||||
<Icon name="mdi:check" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</VaultCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { InsertHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'passwordGroupCreate',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const check = ref(false)
|
||||
|
||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||
const vaultGroup = ref<InsertHaexPasswordsGroups>({
|
||||
name: '',
|
||||
description: '',
|
||||
id: '',
|
||||
color: null,
|
||||
icon: null,
|
||||
order: null,
|
||||
parentId: currentGroupId.value,
|
||||
})
|
||||
|
||||
const errors = ref({
|
||||
name: [],
|
||||
description: [],
|
||||
})
|
||||
|
||||
const onClose = () => {
|
||||
useRouter().back()
|
||||
}
|
||||
|
||||
const onCreate = async () => {
|
||||
try {
|
||||
check.value = true
|
||||
|
||||
if (errors.value.name.length || errors.value.description.length) return
|
||||
|
||||
const { addGroupAsync } = usePasswordGroupStore()
|
||||
|
||||
const newGroup = await addGroupAsync(vaultGroup.value)
|
||||
|
||||
console.log('newGroup', newGroup)
|
||||
if (!newGroup.id) {
|
||||
return
|
||||
}
|
||||
//console.log('created group with id', newGroup?.id)
|
||||
|
||||
//currentGroupId.value = newGroup?.id
|
||||
await navigateTo(
|
||||
useLocalePath()({
|
||||
name: 'passwordGroupItems',
|
||||
params: {
|
||||
groupId: newGroup.id,
|
||||
},
|
||||
query: {
|
||||
...useRoute().query,
|
||||
},
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"title": "Neue Gruppe anlegen",
|
||||
"abort": "Abbrechen",
|
||||
"create": "Anlegen",
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"description": {
|
||||
"label": "Beschreibung"
|
||||
}
|
||||
},
|
||||
|
||||
"en": {
|
||||
"title": "Create new Group",
|
||||
"abort": "Abort",
|
||||
"create": "Create",
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"description": {
|
||||
"label": "Description"
|
||||
}
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
150
src/pages/vault/[vaultId]/passwords/[[groupId]]/edit.vue
Normal file
150
src/pages/vault/[vaultId]/passwords/[[groupId]]/edit.vue
Normal file
@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<div>
|
||||
<VaultGroup
|
||||
@submit="onSaveAsync"
|
||||
@back="onBackAsync"
|
||||
@reject="onRejectAsync"
|
||||
@close="onCloseAsync"
|
||||
v-model="vaultGroup"
|
||||
v-model:read_only="read_only"
|
||||
:originally
|
||||
>
|
||||
<!-- <template #bottom="{ onSubmit, onClose }">
|
||||
<button
|
||||
class="btn btn-error flex-1 flex-nowrap"
|
||||
@click="onClose"
|
||||
type="button"
|
||||
>
|
||||
{{ t('abort') }}
|
||||
<Icon name="mdi:close" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="btn btn-primary flex-1 flex-nowrap"
|
||||
type="button"
|
||||
@click="onSubmit"
|
||||
>
|
||||
{{ t('save') }}
|
||||
<Icon name="mdi:check" />
|
||||
</button>
|
||||
</template> -->
|
||||
</VaultGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router'
|
||||
import type { SelectHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'passwordGroupEdit',
|
||||
})
|
||||
|
||||
const { read_only } = storeToRefs(useVaultStore())
|
||||
|
||||
const vaultGroup = ref<SelectHaexPasswordsGroups>({
|
||||
color: '',
|
||||
description: '',
|
||||
icon: '',
|
||||
id: '',
|
||||
name: '',
|
||||
order: null,
|
||||
parentId: '',
|
||||
createdAt: null,
|
||||
updateAt: null,
|
||||
})
|
||||
|
||||
const originally = ref<SelectHaexPasswordsGroups>()
|
||||
|
||||
const onCloseAsync = async () => {
|
||||
if (read_only.value) return navigateToGroupItemsAsync(vaultGroup.value.id)
|
||||
else read_only.value = true
|
||||
}
|
||||
/* {
|
||||
await navigateTo(
|
||||
useLocaleRoute()({
|
||||
name: 'vaultGroupEntries',
|
||||
params: {
|
||||
...useRouter().currentRoute.value.params,
|
||||
},
|
||||
query: {
|
||||
...useRouter().currentRoute.value.query,
|
||||
},
|
||||
})
|
||||
);
|
||||
}; */
|
||||
|
||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||
const { readGroupAsync, navigateToGroupItemsAsync } = usePasswordGroupStore()
|
||||
|
||||
const getGroupAsync = async () => {
|
||||
if (!currentGroupId.value) return
|
||||
|
||||
const group = await readGroupAsync(currentGroupId.value)
|
||||
console.log('found group', group)
|
||||
if (group) {
|
||||
vaultGroup.value = group
|
||||
originally.value = { ...group }
|
||||
}
|
||||
}
|
||||
watch(currentGroupId, async () => getGroupAsync(), { immediate: true })
|
||||
|
||||
const { add } = useSnackbar()
|
||||
|
||||
const onSaveAsync = async (to?: RouteLocationNormalizedLoadedGeneric) => {
|
||||
try {
|
||||
const { updateAsync } = usePasswordGroupStore()
|
||||
await updateAsync(vaultGroup.value)
|
||||
await getGroupAsync()
|
||||
read_only.value = true
|
||||
if (to) {
|
||||
return navigateTo(to)
|
||||
}
|
||||
} catch (error) {
|
||||
add({
|
||||
type: 'error',
|
||||
text: JSON.stringify(error),
|
||||
})
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
const onBackAsync = async () => {
|
||||
if (originally.value) vaultGroup.value = { ...originally.value }
|
||||
await navigateToGroupItemsAsync(vaultGroup.value.id)
|
||||
}
|
||||
|
||||
const onRejectAsync = async (to?: RouteLocationNormalizedLoadedGeneric) => {
|
||||
if (originally.value) vaultGroup.value = { ...originally.value }
|
||||
if (to) return navigateTo(to)
|
||||
else return onBackAsync
|
||||
}
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
"de": {
|
||||
"title": "Gruppe anpassen",
|
||||
"abort": "Abbrechen",
|
||||
"save": "Speichern",
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"description": {
|
||||
"label": "Beschreibung"
|
||||
}
|
||||
},
|
||||
|
||||
"en": {
|
||||
"title": "Edit Group",
|
||||
"abort": "Abort",
|
||||
"save": "Save",
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"description": {
|
||||
"label": "Description"
|
||||
}
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
40
src/pages/vault/[vaultId]/passwords/[[groupId]]/index.vue
Normal file
40
src/pages/vault/[vaultId]/passwords/[[groupId]]/index.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="relative h-full">
|
||||
<div>
|
||||
<HaexPassMobileMenu :group-items="currentGroupItems" />
|
||||
|
||||
<div
|
||||
class="fixed bottom-4 flex justify-center w-full transition-all pointer-events-none"
|
||||
:class="[isVisible ? 'left-15' : 'left-0']"
|
||||
>
|
||||
<!-- <UiButton class="btn btn-primary btn-lg btn-square rotate-45">
|
||||
<Icon name="mdi:plus" />
|
||||
</UiButton> -->
|
||||
<UiButtonAction
|
||||
class="pointer-events-auto"
|
||||
:menu
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SelectHaexPasswordsItems } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'passwordGroupItems',
|
||||
})
|
||||
|
||||
const { menu } = storeToRefs(usePasswordsActionMenuStore())
|
||||
const items = ref<SelectHaexPasswordsItems[]>([])
|
||||
|
||||
const { readGroupItemsAsync } = usePasswordGroupStore()
|
||||
const { currentGroupItems } = storeToRefs(usePasswordGroupStore())
|
||||
const { isVisible } = storeToRefs(useSidebarStore())
|
||||
|
||||
console.log('currentGroupItems', currentGroupItems.value)
|
||||
const test = () => console.log('currentGroupItems', currentGroupItems.value)
|
||||
|
||||
onMounted(async () => {})
|
||||
</script>
|
||||
@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<div>item</div>
|
||||
</template>
|
||||
@ -0,0 +1 @@
|
||||
<template><div>create item</div></template>
|
||||
@ -29,14 +29,10 @@
|
||||
|
||||
<div class="p-2">{{ t('notifications.label') }}</div>
|
||||
<div class="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="switch switch-primary"
|
||||
:checked="isNotificationAllowed"
|
||||
readonly
|
||||
/>
|
||||
{{ isNotificationAllowed }}
|
||||
<UiButton @click="requestNotificationPermissionAsync">
|
||||
<UiButton
|
||||
class="btn-primary"
|
||||
@click="requestNotificationPermissionAsync"
|
||||
>
|
||||
{{ t('notifications.requestPermission') }}
|
||||
</UiButton>
|
||||
</div>
|
||||
@ -49,7 +45,7 @@ import type { Locale } from 'vue-i18n'
|
||||
import { haexSettings } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'haexSettings',
|
||||
name: 'settings',
|
||||
})
|
||||
|
||||
const { t, setLocale } = useI18n()
|
||||
|
||||
Reference in New Issue
Block a user