mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 06:30:50 +01:00
item handling
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="p-4">
|
||||
<div class="p-2 h-full">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</template>
|
||||
@ -8,6 +8,4 @@
|
||||
definePageMeta({
|
||||
name: 'passwords',
|
||||
})
|
||||
|
||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||
</script>
|
||||
|
||||
@ -1,78 +1,32 @@
|
||||
<template>
|
||||
<div>
|
||||
<VaultCard
|
||||
:title="t('title')"
|
||||
icon="mdi:folder-plus-outline"
|
||||
<HaexPassGroup
|
||||
v-model="group"
|
||||
mode="create"
|
||||
@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>
|
||||
@submit="createAsync"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { InsertHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||
import type { SelectHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'passwordGroupCreate',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const check = ref(false)
|
||||
|
||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||
const vaultGroup = ref<InsertHaexPasswordsGroups>({
|
||||
const group = ref<SelectHaexPasswordsGroups>({
|
||||
name: '',
|
||||
description: '',
|
||||
id: '',
|
||||
color: null,
|
||||
icon: null,
|
||||
order: null,
|
||||
parentId: currentGroupId.value,
|
||||
parentId: currentGroupId.value || null,
|
||||
createdAt: null,
|
||||
updateAt: null,
|
||||
})
|
||||
|
||||
const errors = ref({
|
||||
@ -84,23 +38,18 @@ const onClose = () => {
|
||||
useRouter().back()
|
||||
}
|
||||
|
||||
const onCreate = async () => {
|
||||
const { addGroupAsync } = usePasswordGroupStore()
|
||||
const createAsync = async () => {
|
||||
try {
|
||||
check.value = true
|
||||
|
||||
if (errors.value.name.length || errors.value.description.length) return
|
||||
|
||||
const { addGroupAsync } = usePasswordGroupStore()
|
||||
|
||||
const newGroup = await addGroupAsync(vaultGroup.value)
|
||||
const newGroup = await addGroupAsync(group.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',
|
||||
@ -117,31 +66,3 @@ const onCreate = async () => {
|
||||
}
|
||||
}
|
||||
</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>
|
||||
|
||||
@ -1,150 +1,85 @@
|
||||
<template>
|
||||
<div>
|
||||
<VaultGroup
|
||||
<HaexPassGroup
|
||||
v-model="currentGroup"
|
||||
mode="edit"
|
||||
@close="onClose"
|
||||
@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 { t } = useI18n()
|
||||
|
||||
const vaultGroup = ref<SelectHaexPasswordsGroups>({
|
||||
color: '',
|
||||
description: '',
|
||||
icon: '',
|
||||
id: '',
|
||||
name: '',
|
||||
order: null,
|
||||
parentId: '',
|
||||
createdAt: null,
|
||||
updateAt: null,
|
||||
const check = ref(false)
|
||||
|
||||
const { currentGroup } = storeToRefs(usePasswordGroupStore())
|
||||
|
||||
//const group = computed(() => currentGroup.value)
|
||||
|
||||
const errors = ref({
|
||||
name: [],
|
||||
description: [],
|
||||
})
|
||||
|
||||
const originally = ref<SelectHaexPasswordsGroups>()
|
||||
|
||||
const onCloseAsync = async () => {
|
||||
if (read_only.value) return navigateToGroupItemsAsync(vaultGroup.value.id)
|
||||
else read_only.value = true
|
||||
const onClose = () => {
|
||||
useRouter().back()
|
||||
}
|
||||
/* {
|
||||
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) => {
|
||||
const onSaveAsync = async () => {
|
||||
try {
|
||||
check.value = true
|
||||
if (!currentGroup.value) return
|
||||
|
||||
console.log('onSave', errors.value)
|
||||
if (errors.value.name.length || errors.value.description.length) return
|
||||
|
||||
const { updateAsync } = usePasswordGroupStore()
|
||||
await updateAsync(vaultGroup.value)
|
||||
await getGroupAsync()
|
||||
read_only.value = true
|
||||
if (to) {
|
||||
return navigateTo(to)
|
||||
}
|
||||
|
||||
await updateAsync(currentGroup.value)
|
||||
|
||||
add({ type: 'success', text: t('change.success') })
|
||||
onClose()
|
||||
} catch (error) {
|
||||
add({
|
||||
type: 'error',
|
||||
text: JSON.stringify(error),
|
||||
})
|
||||
add({ type: 'error', text: t('change.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"
|
||||
}
|
||||
},
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
title: Gruppe ändern
|
||||
abort: Abbrechen
|
||||
save: Speichern
|
||||
name:
|
||||
label: Name
|
||||
|
||||
"en": {
|
||||
"title": "Edit Group",
|
||||
"abort": "Abort",
|
||||
"save": "Save",
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"description": {
|
||||
"label": "Description"
|
||||
}
|
||||
}
|
||||
}
|
||||
description:
|
||||
label: Beschreibung
|
||||
|
||||
change:
|
||||
success: Änderung erfolgreich gespeichert
|
||||
error: Änderung konnte nicht gespeichert werden
|
||||
|
||||
en:
|
||||
title: Edit Group
|
||||
abort: Abort
|
||||
save: Save
|
||||
name:
|
||||
label: Name
|
||||
|
||||
description:
|
||||
label: Description
|
||||
|
||||
change:
|
||||
success: Change successfully saved
|
||||
error: Change could not be saved
|
||||
</i18n>
|
||||
|
||||
@ -1,40 +1,228 @@
|
||||
<template>
|
||||
<div class="relative h-full">
|
||||
<div>
|
||||
<HaexPassMobileMenu :group-items="currentGroupItems" />
|
||||
<div class="h-full">
|
||||
<div class="h-full overflow-auto p-1 flex flex-col">
|
||||
<HaexPassGroupBreadcrumbs
|
||||
:items="breadCrumbs"
|
||||
v-show="breadCrumbs.length"
|
||||
/>
|
||||
|
||||
<HaexPassMobileMenu
|
||||
ref="listRef"
|
||||
:menu-items="groupItems"
|
||||
v-model:selected-items="selectedItems"
|
||||
sel
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="fixed bottom-4 flex justify-center w-full transition-all pointer-events-none"
|
||||
:class="[isVisible ? 'left-15' : 'left-0']"
|
||||
class="fixed bottom-4 flex justify-between transition-all pointer-events-none right-0 sm:items-center items-end"
|
||||
:class="[isVisible ? 'left-15 ' : 'left-0']"
|
||||
>
|
||||
<!-- <UiButton class="btn btn-primary btn-lg btn-square rotate-45">
|
||||
<Icon name="mdi:plus" />
|
||||
</UiButton> -->
|
||||
<div class="w-full pl-8"></div>
|
||||
|
||||
<UiButtonAction
|
||||
class="pointer-events-auto"
|
||||
:menu
|
||||
v-if="!inTrashGroup"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="flex flex-col sm:flex-row gap-4 w-full justify-end items-end pr-8"
|
||||
>
|
||||
<UiButton
|
||||
v-show="selectedItems.size === 1"
|
||||
class="btn-square btn-accent"
|
||||
@click="onEditAsync"
|
||||
:tooltip="t('edit')"
|
||||
>
|
||||
<Icon name="mdi:pencil" />
|
||||
</UiButton>
|
||||
|
||||
<UiButton
|
||||
class="btn-square btn-accent"
|
||||
v-show="selectedItems.size"
|
||||
@click="onCut"
|
||||
:tooltip="t('cut')"
|
||||
>
|
||||
<Icon name="mdi:scissors" />
|
||||
</UiButton>
|
||||
|
||||
<UiButton
|
||||
class="btn-square btn-accent"
|
||||
v-show="selectedGroupItems?.length"
|
||||
@click="onPasteAsync"
|
||||
:tooltip="t('paste')"
|
||||
>
|
||||
<Icon name="proicons:clipboard-paste" />
|
||||
</UiButton>
|
||||
|
||||
<UiButton
|
||||
v-show="selectedItems.size"
|
||||
class="btn-square btn-accent"
|
||||
@click="onDeleteAsync"
|
||||
:tooltip="t('delete')"
|
||||
>
|
||||
<Icon name="mdi:trash" />
|
||||
</UiButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SelectHaexPasswordsItems } from '~~/src-tauri/database/schemas/vault'
|
||||
import type { IPasswordMenuItem } from '~/components/haex/pass/mobile/menu/types'
|
||||
import { useMagicKeys } from '@vueuse/core'
|
||||
|
||||
definePageMeta({
|
||||
name: 'passwordGroupItems',
|
||||
})
|
||||
|
||||
const selectedItems = ref<Set<IPasswordMenuItem>>(new Set())
|
||||
const { menu } = storeToRefs(usePasswordsActionMenuStore())
|
||||
const items = ref<SelectHaexPasswordsItems[]>([])
|
||||
|
||||
const { readGroupItemsAsync } = usePasswordGroupStore()
|
||||
const { currentGroupItems } = storeToRefs(usePasswordGroupStore())
|
||||
const {
|
||||
currentGroupItems,
|
||||
breadCrumbs,
|
||||
selectedGroupItems,
|
||||
currentGroupId,
|
||||
inTrashGroup,
|
||||
} = storeToRefs(usePasswordGroupStore())
|
||||
const { insertGroupItemsAsync } = usePasswordGroupStore()
|
||||
|
||||
const groupItems = computed<IPasswordMenuItem[]>(() => {
|
||||
const items: IPasswordMenuItem[] = []
|
||||
|
||||
items.push(
|
||||
...currentGroupItems.value.groups.map<IPasswordMenuItem>((group) => ({
|
||||
name: group.name,
|
||||
id: group.id,
|
||||
icon: group.icon,
|
||||
type: 'group',
|
||||
color: group.color,
|
||||
})),
|
||||
)
|
||||
|
||||
items.push(
|
||||
...currentGroupItems.value.items.map<IPasswordMenuItem>((item) => ({
|
||||
name: item.title,
|
||||
id: item.id,
|
||||
icon: item.icon,
|
||||
type: 'item',
|
||||
})),
|
||||
)
|
||||
return items
|
||||
})
|
||||
|
||||
const { isVisible } = storeToRefs(useSidebarStore())
|
||||
|
||||
console.log('currentGroupItems', currentGroupItems.value)
|
||||
const test = () => console.log('currentGroupItems', currentGroupItems.value)
|
||||
const onEditAsync = async () => {
|
||||
const item = selectedItems.value.values().next().value
|
||||
console.log('onEditAsync', item)
|
||||
if (item?.type === 'group')
|
||||
await navigateTo(
|
||||
useLocalePath()({
|
||||
name: 'passwordGroupEdit',
|
||||
params: { groupId: item.id },
|
||||
}),
|
||||
)
|
||||
else if (item?.type === 'item') {
|
||||
await navigateTo(
|
||||
useLocalePath()({
|
||||
name: 'passwordItemEdit',
|
||||
params: { itemId: item.id },
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
onKeyStroke('e', async (e) => {
|
||||
if (e.ctrlKey) {
|
||||
await onEditAsync()
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(async () => {})
|
||||
const onCut = () => {
|
||||
selectedGroupItems.value = [...selectedItems.value]
|
||||
selectedItems.value.clear()
|
||||
}
|
||||
onKeyStroke('x', (event) => {
|
||||
if (event.ctrlKey && selectedItems.value.size) {
|
||||
event.preventDefault()
|
||||
onCut()
|
||||
}
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const { add } = useSnackbar()
|
||||
|
||||
const onPasteAsync = async () => {
|
||||
if (!selectedGroupItems.value?.length) return
|
||||
|
||||
try {
|
||||
await insertGroupItemsAsync(
|
||||
[...selectedGroupItems.value],
|
||||
currentGroupId.value,
|
||||
)
|
||||
await syncGroupItemsAsync(currentGroupId.value)
|
||||
selectedGroupItems.value = []
|
||||
selectedItems.value.clear()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
selectedGroupItems.value = []
|
||||
add({ type: 'error', text: t('error.paste') })
|
||||
}
|
||||
}
|
||||
onKeyStroke('v', async (event) => {
|
||||
if (event.ctrlKey) {
|
||||
event.preventDefault()
|
||||
await onPasteAsync()
|
||||
}
|
||||
})
|
||||
|
||||
const { escape } = useMagicKeys()
|
||||
watch(escape, () => {
|
||||
selectedItems.value.clear()
|
||||
})
|
||||
|
||||
onKeyStroke('a', (event) => {
|
||||
if (event.ctrlKey) {
|
||||
event.preventDefault()
|
||||
selectedItems.value = new Set(groupItems.value)
|
||||
}
|
||||
})
|
||||
|
||||
const { deleteAsync } = usePasswordItemStore()
|
||||
const { deleteGroupAsync, syncGroupItemsAsync } = usePasswordGroupStore()
|
||||
const onDeleteAsync = async () => {
|
||||
for (const item of selectedItems.value) {
|
||||
if (item.type === 'group') {
|
||||
await deleteGroupAsync(item.id, inTrashGroup.value)
|
||||
}
|
||||
if (item.type === 'item') {
|
||||
await deleteAsync(item.id, inTrashGroup.value)
|
||||
}
|
||||
}
|
||||
selectedItems.value.clear()
|
||||
await syncGroupItemsAsync(currentGroupId.value)
|
||||
}
|
||||
const keys = useMagicKeys()
|
||||
watch(keys.delete, async () => {
|
||||
await onDeleteAsync()
|
||||
})
|
||||
|
||||
const listRef = useTemplateRef<HTMLElement>('listRef')
|
||||
onClickOutside(listRef, () => setTimeout(() => selectedItems.value.clear(), 50))
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
cut: Ausschneiden
|
||||
paste: Einfügen
|
||||
delete: Löschen
|
||||
edit: Bearbeiten
|
||||
en:
|
||||
cut: Cut
|
||||
paste: Paste
|
||||
delete: Delete
|
||||
edit: Edit
|
||||
</i18n>
|
||||
|
||||
@ -1,3 +1,297 @@
|
||||
<template>
|
||||
<div>item</div>
|
||||
<div>
|
||||
<HaexPassItem
|
||||
:history="item.history"
|
||||
:read_only
|
||||
@close="onClose"
|
||||
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"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="fixed bottom-4 flex justify-between transition-all pointer-events-none right-0 sm:items-center items-end"
|
||||
:class="[isVisible ? 'left-15 ' : 'left-0']"
|
||||
>
|
||||
<div class="flex items-center justify-center w-full">
|
||||
<UiTooltip :tooltip="t('abort')">
|
||||
<UiButton
|
||||
class="btn-accent btn-square"
|
||||
@click="onClose"
|
||||
>
|
||||
<Icon name="mdi:close" />
|
||||
</UiButton>
|
||||
</UiTooltip>
|
||||
</div>
|
||||
|
||||
<UiTooltip
|
||||
v-show="read_only && !hasChanges"
|
||||
:tooltip="t('edit')"
|
||||
>
|
||||
<UiButton
|
||||
class="btn-xl btn-square btn-primary"
|
||||
@click="read_only = false"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:pencil-outline"
|
||||
class="size-11 shrink-0"
|
||||
/>
|
||||
</UiButton>
|
||||
</UiTooltip>
|
||||
|
||||
<UiTooltip
|
||||
v-show="!read_only && !hasChanges"
|
||||
:tooltip="t('noEdit')"
|
||||
>
|
||||
<UiButton
|
||||
class="btn-xl btn-square btn-primary"
|
||||
@click="read_only = true"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:pencil-off-outline"
|
||||
class="size-11 shrink-0"
|
||||
/>
|
||||
</UiButton>
|
||||
</UiTooltip>
|
||||
|
||||
<UiTooltip
|
||||
:tooltip="t('save')"
|
||||
v-show="!read_only && hasChanges"
|
||||
>
|
||||
<UiButton
|
||||
class="btn-xl btn-square btn-primary motion-duration-2000"
|
||||
:class="{ 'motion-preset-pulse-sm': hasChanges }"
|
||||
@click="onUpdateAsync"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:content-save-outline"
|
||||
class="size-11 shrink-0"
|
||||
/>
|
||||
</UiButton>
|
||||
</UiTooltip>
|
||||
|
||||
<div class="flex items-center justify-center w-full">
|
||||
<UiTooltip :tooltip="t('delete')">
|
||||
<UiButton
|
||||
class="btn-square btn-error"
|
||||
@click="showConfirmDeleteDialog = true"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:trash-outline"
|
||||
class="shrink-0"
|
||||
/>
|
||||
</UiButton>
|
||||
</UiTooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UiDialogConfirm
|
||||
v-model:open="showConfirmDeleteDialog"
|
||||
:confirm-label="t('dialog.delete.label')"
|
||||
:title="t('dialog.delete.title')"
|
||||
@abort="showConfirmDeleteDialog = false"
|
||||
@confirm="deleteItemAsync"
|
||||
>
|
||||
{{ t('dialog.delete.question') }}
|
||||
</UiDialogConfirm>
|
||||
|
||||
<UiDialogConfirm
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
:confirm-label="t('dialog.unsavedChanges.label')"
|
||||
:title="t('dialog.unsavedChanges.title')"
|
||||
@abort="showUnsavedChangesDialog = false"
|
||||
@confirm="onConfirmIgnoreChanges"
|
||||
>
|
||||
{{ t('dialog.unsavedChanges.question') }}
|
||||
</UiDialogConfirm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type {
|
||||
SelectHaexPasswordsItemDetails,
|
||||
SelectHaexPasswordsItemHistory,
|
||||
SelectHaexPasswordsItemKeyValues,
|
||||
} from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'passwordItemEdit',
|
||||
})
|
||||
|
||||
defineProps({
|
||||
icon: String,
|
||||
title: String,
|
||||
withCopyButton: Boolean,
|
||||
})
|
||||
|
||||
const { isVisible } = storeToRefs(useSidebarStore())
|
||||
const read_only = ref(true)
|
||||
const showConfirmDeleteDialog = ref(false)
|
||||
const { t } = useI18n()
|
||||
|
||||
const item = reactive<{
|
||||
details: SelectHaexPasswordsItemDetails
|
||||
history: SelectHaexPasswordsItemHistory[]
|
||||
keyValues: SelectHaexPasswordsItemKeyValues[]
|
||||
keyValuesAdd: SelectHaexPasswordsItemKeyValues[]
|
||||
keyValuesDelete: SelectHaexPasswordsItemKeyValues[]
|
||||
originalDetails: string | null
|
||||
originalKeyValues: string | null
|
||||
}>({
|
||||
details: {
|
||||
id: '',
|
||||
createdAt: null,
|
||||
icon: null,
|
||||
note: null,
|
||||
password: null,
|
||||
tags: null,
|
||||
title: null,
|
||||
updateAt: null,
|
||||
url: null,
|
||||
username: null,
|
||||
},
|
||||
keyValues: [],
|
||||
history: [],
|
||||
keyValuesAdd: [],
|
||||
keyValuesDelete: [],
|
||||
originalDetails: null,
|
||||
originalKeyValues: null,
|
||||
})
|
||||
|
||||
const { currentItem } = storeToRefs(usePasswordItemStore())
|
||||
|
||||
watch(currentItem, () => {
|
||||
item.details = JSON.parse(JSON.stringify(currentItem.value?.details))
|
||||
item.keyValues = JSON.parse(JSON.stringify(currentItem.value?.keyValues))
|
||||
item.history = JSON.parse(JSON.stringify(currentItem.value?.history))
|
||||
item.keyValuesAdd = []
|
||||
item.keyValuesDelete = []
|
||||
item.originalDetails = JSON.stringify(currentItem.value?.details)
|
||||
item.originalKeyValues = JSON.stringify(currentItem.value?.keyValues)
|
||||
ignoreChanges.value = false
|
||||
})
|
||||
|
||||
const { add } = useSnackbar()
|
||||
const { deleteAsync, updateAsync } = usePasswordItemStore()
|
||||
const { syncGroupItemsAsync, trashId } = usePasswordGroupStore()
|
||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||
|
||||
const onUpdateAsync = async () => {
|
||||
try {
|
||||
const newId = await updateAsync({
|
||||
details: item.details,
|
||||
groupId: currentGroupId.value || null,
|
||||
keyValues: item.keyValues,
|
||||
keyValuesAdd: item.keyValuesAdd,
|
||||
keyValuesDelete: item.keyValuesDelete,
|
||||
})
|
||||
if (newId) add({ type: 'success', text: t('success.update') })
|
||||
syncGroupItemsAsync(currentGroupId.value)
|
||||
ignoreChanges.value = true
|
||||
onClose()
|
||||
} catch (error) {
|
||||
add({ type: 'error', text: t('error.update') })
|
||||
}
|
||||
}
|
||||
|
||||
const onClose = () => {
|
||||
if (showConfirmDeleteDialog.value || showUnsavedChangesDialog.value) return
|
||||
|
||||
if (hasChanges.value && !ignoreChanges.value)
|
||||
return (showUnsavedChangesDialog.value = true)
|
||||
|
||||
ignoreChanges.value = false
|
||||
read_only.value = true
|
||||
useRouter().back()
|
||||
}
|
||||
|
||||
const deleteItemAsync = async () => {
|
||||
try {
|
||||
await deleteAsync(item.details.id, currentGroupId.value === trashId)
|
||||
showConfirmDeleteDialog.value = false
|
||||
add({ type: 'success', text: t('success.delete') })
|
||||
syncGroupItemsAsync(currentGroupId.value)
|
||||
onClose()
|
||||
} catch (errro) {
|
||||
add({
|
||||
type: 'error',
|
||||
text: t('error.delete'),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const hasChanges = computed(
|
||||
() =>
|
||||
!!(
|
||||
item.originalDetails !== JSON.stringify(item.details) ||
|
||||
item.originalKeyValues !== JSON.stringify(item.keyValues) ||
|
||||
item.keyValuesAdd.length ||
|
||||
item.keyValuesDelete.length
|
||||
),
|
||||
)
|
||||
|
||||
const showUnsavedChangesDialog = ref(false)
|
||||
const ignoreChanges = ref(false)
|
||||
|
||||
const onConfirmIgnoreChanges = () => {
|
||||
ignoreChanges.value = true
|
||||
showUnsavedChangesDialog.value = false
|
||||
onClose()
|
||||
}
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
save: Speichern
|
||||
abort: Abbrechen
|
||||
edit: Bearbeiten
|
||||
noEdit: Lesemodus
|
||||
delete: Löschen
|
||||
success:
|
||||
update: Eintrag erfolgreich aktualisiert
|
||||
delete: Eintrag wurde gelöscht
|
||||
error:
|
||||
update: Eintrag konnte nicht aktualisiert werden
|
||||
delete: Eintrag konnte nicht gelöscht werden
|
||||
tab:
|
||||
details: Details
|
||||
keyValue: Extra
|
||||
history: Verlauf
|
||||
|
||||
dialog:
|
||||
delete:
|
||||
title: Eintrag löschen
|
||||
question: Soll der Eintrag wirklich gelöscht werden?
|
||||
label: Löschen
|
||||
unsavedChanges:
|
||||
title: Nicht gespeicherte Änderungen
|
||||
question: Sollen die Änderungen verworfen werden?
|
||||
label: Verwerfen
|
||||
en:
|
||||
save: Save
|
||||
abort: Abort
|
||||
edit: Edit
|
||||
noEdit: Read Mode
|
||||
delete: Delete
|
||||
success:
|
||||
update: Entry successfully updated
|
||||
delete: Entry successfully removed
|
||||
error:
|
||||
update: Entry could not be updated
|
||||
delete: Entry could not be deleted
|
||||
tab:
|
||||
details: Details
|
||||
keyValue: Extra
|
||||
history: History
|
||||
|
||||
dialog:
|
||||
delete:
|
||||
title: Delete Entry
|
||||
question: Should the entry really be deleted?
|
||||
label: Delete
|
||||
unsavedChanges:
|
||||
title: Unsaved changes
|
||||
question: Should the changes be discarded?
|
||||
label: discard
|
||||
</i18n>
|
||||
|
||||
@ -1 +1,133 @@
|
||||
<template><div>create item</div></template>
|
||||
<template>
|
||||
<div>
|
||||
<HaexPassItem
|
||||
:default-icon="currentGroup?.icon"
|
||||
:history="item.history"
|
||||
@close="onClose"
|
||||
v-model:details="item.details"
|
||||
v-model:key-values-add="item.keyValuesAdd"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="fixed bottom-4 flex justify-between transition-all pointer-events-none right-15 sm:items-center items-end"
|
||||
:class="[isVisible ? 'left-15 ' : 'left-0']"
|
||||
>
|
||||
<div class="flex items-center justify-center w-full">
|
||||
<UiTooltip :tooltip="t('abort')">
|
||||
<UiButton
|
||||
class="btn-error btn-square"
|
||||
@click="onClose"
|
||||
>
|
||||
<Icon name="mdi:close" />
|
||||
</UiButton>
|
||||
</UiTooltip>
|
||||
</div>
|
||||
<UiTooltip :tooltip="t('create')">
|
||||
<UiButton
|
||||
class="btn-xl btn-square btn-primary"
|
||||
@click="onCreateAsync"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:content-save-outline"
|
||||
class="size-11 shrink-0"
|
||||
/>
|
||||
</UiButton>
|
||||
</UiTooltip>
|
||||
<div class="flex items-center justify-center w-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type {
|
||||
SelectHaexPasswordsItemDetails,
|
||||
SelectHaexPasswordsItemHistory,
|
||||
SelectHaexPasswordsItemKeyValues,
|
||||
} from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
definePageMeta({
|
||||
name: 'passwordItemCreate',
|
||||
})
|
||||
|
||||
defineProps({
|
||||
icon: String,
|
||||
title: String,
|
||||
withCopyButton: Boolean,
|
||||
})
|
||||
|
||||
const { isVisible } = storeToRefs(useSidebarStore())
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const item = reactive<{
|
||||
details: SelectHaexPasswordsItemDetails
|
||||
history: SelectHaexPasswordsItemHistory[]
|
||||
keyValuesAdd: SelectHaexPasswordsItemKeyValues[]
|
||||
keyValuesDelete: SelectHaexPasswordsItemKeyValues[]
|
||||
originalDetails: string | null
|
||||
originalKeyValues: string | null
|
||||
}>({
|
||||
details: {
|
||||
id: '',
|
||||
createdAt: null,
|
||||
icon: null,
|
||||
note: null,
|
||||
password: null,
|
||||
tags: null,
|
||||
title: null,
|
||||
updateAt: null,
|
||||
url: null,
|
||||
username: null,
|
||||
},
|
||||
history: [],
|
||||
keyValuesAdd: [],
|
||||
keyValuesDelete: [],
|
||||
originalDetails: null,
|
||||
originalKeyValues: null,
|
||||
})
|
||||
|
||||
const { add } = useSnackbar()
|
||||
const { currentGroup } = storeToRefs(usePasswordGroupStore())
|
||||
const { syncGroupItemsAsync } = usePasswordGroupStore()
|
||||
const { addAsync } = usePasswordItemStore()
|
||||
|
||||
const onCreateAsync = async () => {
|
||||
try {
|
||||
const newId = await addAsync(
|
||||
item.details,
|
||||
item.keyValuesAdd,
|
||||
currentGroup.value,
|
||||
)
|
||||
if (newId) {
|
||||
add({ type: 'success', text: t('success') })
|
||||
syncGroupItemsAsync(currentGroup.value?.id)
|
||||
onClose()
|
||||
}
|
||||
} catch (error) {
|
||||
add({ type: 'error', text: t('error') })
|
||||
}
|
||||
}
|
||||
|
||||
const onClose = () => useRouter().back()
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
create: Anlegen
|
||||
abort: Abbrechen
|
||||
success: Eintrag erfolgreich erstellt
|
||||
error: Eintrag konnte nicht erstellt werden
|
||||
tab:
|
||||
details: Details
|
||||
keyValue: Extra
|
||||
history: Verlauf
|
||||
en:
|
||||
create: Create
|
||||
abort: Abort
|
||||
success: Entry successfully created
|
||||
error: Entry could not be created
|
||||
tab:
|
||||
details: Details
|
||||
keyValue: Extra
|
||||
history: History
|
||||
</i18n>
|
||||
|
||||
Reference in New Issue
Block a user