mobile menu

This commit is contained in:
2025-06-08 00:08:55 +02:00
parent 0f09bf8436
commit 18fee933ec
68 changed files with 4112 additions and 416 deletions

View File

@ -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',
})

View File

@ -1,5 +1,5 @@
<template>
<div class="w-full h-full">
<div class="h-full w-full">
<NuxtLayout name="app">
<NuxtPage />
</NuxtLayout>

View File

@ -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>

View File

@ -1,8 +0,0 @@
<template>
<div class="flex">
<HaexPassSidebar />
<div>
<NuxtPage />
</div>
</div>
</template>

View File

@ -1,9 +0,0 @@
<template>
<div>passwords</div>
</template>
<script setup lang="ts">
definePageMeta({
name: "haexpassOverview"
})
</script>

View 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>

View 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>

View 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>

View 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>

View 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>

View File

@ -0,0 +1,3 @@
<template>
<div>item</div>
</template>

View File

@ -0,0 +1 @@
<template><div>create item</div></template>

View File

@ -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()