mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 06:30:50 +01:00
implemented search
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="p-2 min-h-full">
|
||||
<div class="h-full overflow-auto p-2">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,11 +1,29 @@
|
||||
<template>
|
||||
<div>
|
||||
{{ currentGroupId }}
|
||||
<HaexPassGroup
|
||||
v-model="group"
|
||||
mode="create"
|
||||
@close="onClose"
|
||||
@submit="createAsync"
|
||||
/>
|
||||
|
||||
<HaexPassMenuBottom
|
||||
@close="onClose"
|
||||
@save="createAsync"
|
||||
show-close-button
|
||||
show-save-button
|
||||
:has-changes
|
||||
>
|
||||
</HaexPassMenuBottom>
|
||||
|
||||
<HaexPassDialogUnsavedChanges
|
||||
:has-changes
|
||||
@abort="showUnsavedChangesDialog = false"
|
||||
@confirm="onConfirmIgnoreChanges"
|
||||
v-model:ignore-changes="ignoreChanges"
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -34,7 +52,14 @@ const errors = ref({
|
||||
description: [],
|
||||
})
|
||||
|
||||
const ignoreChanges = ref(false)
|
||||
|
||||
const onClose = () => {
|
||||
if (showUnsavedChangesDialog.value) return
|
||||
|
||||
if (hasChanges.value && !ignoreChanges.value) {
|
||||
return (showUnsavedChangesDialog.value = true)
|
||||
}
|
||||
useRouter().back()
|
||||
}
|
||||
|
||||
@ -45,11 +70,11 @@ const createAsync = async () => {
|
||||
|
||||
const newGroup = await addGroupAsync(group.value)
|
||||
|
||||
console.log('newGroup', newGroup)
|
||||
if (!newGroup.id) {
|
||||
return
|
||||
}
|
||||
|
||||
ignoreChanges.value = true
|
||||
await navigateTo(
|
||||
useLocalePath()({
|
||||
name: 'passwordGroupItems',
|
||||
@ -65,4 +90,20 @@ const createAsync = async () => {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
const hasChanges = computed(() => {
|
||||
return !!(
|
||||
group.value.color ||
|
||||
group.value.description ||
|
||||
group.value.icon ||
|
||||
group.value.name
|
||||
)
|
||||
})
|
||||
|
||||
const showUnsavedChangesDialog = ref(false)
|
||||
const onConfirmIgnoreChanges = () => {
|
||||
showUnsavedChangesDialog.value = false
|
||||
ignoreChanges.value = true
|
||||
onClose()
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,11 +1,43 @@
|
||||
<template>
|
||||
<div>
|
||||
currentGroup{{ currentGroup }}
|
||||
<HaexPassGroup
|
||||
v-model="group"
|
||||
mode="edit"
|
||||
:read_only
|
||||
@close="onClose"
|
||||
@submit="onSaveAsync"
|
||||
mode="edit"
|
||||
v-model="group"
|
||||
/>
|
||||
|
||||
<HaexPassMenuBottom
|
||||
:show-edit-button="read_only && !hasChanges"
|
||||
:show-readonly-button="!read_only && !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>
|
||||
|
||||
<HaexPassDialogDeleteItem
|
||||
v-model:open="showConfirmDeleteDialog"
|
||||
@abort="showConfirmDeleteDialog = false"
|
||||
@confirm="onDeleteAsync"
|
||||
:item-name="group.name"
|
||||
:final="inTrashGroup"
|
||||
>
|
||||
</HaexPassDialogDeleteItem>
|
||||
|
||||
<HaexPassDialogUnsavedChanges
|
||||
:has-changes="hasChanges"
|
||||
v-model:ignore-changes="ignoreChanges"
|
||||
@abort="showUnsavedChangesDialog = false"
|
||||
@confirm="onConfirmIgnoreChanges"
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -19,42 +51,79 @@ definePageMeta({
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const check = ref(false)
|
||||
const { currentGroup, inTrashGroup, currentGroupId } = storeToRefs(
|
||||
usePasswordGroupStore(),
|
||||
)
|
||||
|
||||
const { currentGroup } = storeToRefs(usePasswordGroupStore())
|
||||
const group = ref<SelectHaexPasswordsGroups>({
|
||||
color: null,
|
||||
createdAt: null,
|
||||
description: null,
|
||||
icon: null,
|
||||
id: '',
|
||||
name: null,
|
||||
order: null,
|
||||
parentId: null,
|
||||
updateAt: null,
|
||||
})
|
||||
|
||||
const group = ref<SelectHaexPasswordsGroups>()
|
||||
const original = ref<string>('')
|
||||
const ignoreChanges = ref(false)
|
||||
|
||||
const { readGroupAsync } = usePasswordGroupStore()
|
||||
watch(
|
||||
currentGroup,
|
||||
() => {
|
||||
group.value = JSON.parse(JSON.stringify(currentGroup.value))
|
||||
currentGroupId,
|
||||
async () => {
|
||||
if (!currentGroupId.value) return
|
||||
ignoreChanges.value = false
|
||||
try {
|
||||
const foundGroup = await readGroupAsync(currentGroupId.value)
|
||||
if (foundGroup) {
|
||||
original.value = JSON.stringify(foundGroup)
|
||||
group.value = foundGroup
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
/* watch(
|
||||
currentGroup,
|
||||
(n, o) => {
|
||||
console.log('currentGroup', currentGroup.value, n, o)
|
||||
original.value = JSON.stringify(currentGroup.value)
|
||||
group.value = JSON.parse(original.value)
|
||||
ignoreChanges.value = false
|
||||
},
|
||||
{ immediate: true },
|
||||
) */
|
||||
|
||||
const errors = ref({
|
||||
name: [],
|
||||
description: [],
|
||||
})
|
||||
const read_only = ref(true)
|
||||
|
||||
const hasChanges = computed(
|
||||
() => JSON.stringify(group.value) !== original.value,
|
||||
)
|
||||
|
||||
const onClose = () => {
|
||||
if (showConfirmDeleteDialog.value || showUnsavedChangesDialog.value) return
|
||||
|
||||
read_only.value = true
|
||||
useRouter().back()
|
||||
}
|
||||
|
||||
const { add } = useSnackbar()
|
||||
|
||||
const { updateAsync, syncGroupItemsAsync } = usePasswordGroupStore()
|
||||
const { updateAsync, syncGroupItemsAsync, deleteGroupAsync } =
|
||||
usePasswordGroupStore()
|
||||
|
||||
const onSaveAsync = async () => {
|
||||
try {
|
||||
check.value = true
|
||||
if (!group.value) return
|
||||
|
||||
if (errors.value.name.length || errors.value.description.length) return
|
||||
|
||||
ignoreChanges.value = true
|
||||
await updateAsync(group.value)
|
||||
syncGroupItemsAsync()
|
||||
await syncGroupItemsAsync(group.value.id)
|
||||
add({ type: 'success', text: t('change.success') })
|
||||
onClose()
|
||||
} catch (error) {
|
||||
@ -62,6 +131,34 @@ const onSaveAsync = async () => {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
const showConfirmDeleteDialog = ref(false)
|
||||
const showUnsavedChangesDialog = ref(false)
|
||||
const onConfirmIgnoreChanges = () => {
|
||||
showUnsavedChangesDialog.value = false
|
||||
onClose()
|
||||
}
|
||||
|
||||
const onDeleteAsync = async () => {
|
||||
try {
|
||||
const parentId = group.value.parentId
|
||||
await deleteGroupAsync(group.value.id, inTrashGroup.value)
|
||||
await syncGroupItemsAsync(parentId)
|
||||
showConfirmDeleteDialog.value = false
|
||||
ignoreChanges.value = true
|
||||
await navigateTo(
|
||||
useLocalePath()({
|
||||
name: 'passwordGroupItems',
|
||||
params: {
|
||||
...useRouter().currentRoute.value.params,
|
||||
groupId: parentId,
|
||||
},
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="h-full relative">
|
||||
<div class="h-full">
|
||||
<div class="h-full flex flex-col">
|
||||
<HaexPassGroupBreadcrumbs
|
||||
:items="breadCrumbs"
|
||||
class="px-2 z-10 bg-base-200"
|
||||
class="px-2"
|
||||
v-show="breadCrumbs.length"
|
||||
/>
|
||||
<div class="h-full overflow-auto flex flex-col">
|
||||
<div class="flex-1 overflow-auto">
|
||||
<HaexPassMobileMenu
|
||||
:menu-items="groupItems"
|
||||
ref="listRef"
|
||||
@ -76,40 +76,77 @@ definePageMeta({
|
||||
name: 'passwordGroupItems',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const { add } = useSnackbar()
|
||||
|
||||
const selectedItems = ref<Set<IPasswordMenuItem>>(new Set())
|
||||
const { menu } = storeToRefs(usePasswordsActionMenuStore())
|
||||
|
||||
const { syncItemsAsync } = usePasswordItemStore()
|
||||
const { syncGroupItemsAsync } = usePasswordGroupStore()
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await Promise.allSettled([syncItemsAsync(), syncGroupItemsAsync()])
|
||||
} catch (error) {}
|
||||
})
|
||||
|
||||
const {
|
||||
breadCrumbs,
|
||||
currentGroupId,
|
||||
currentGroupItems,
|
||||
inTrashGroup,
|
||||
selectedGroupItems,
|
||||
groups,
|
||||
} = storeToRefs(usePasswordGroupStore())
|
||||
const { insertGroupItemsAsync } = usePasswordGroupStore()
|
||||
|
||||
const { items } = storeToRefs(usePasswordItemStore())
|
||||
const { search } = storeToRefs(useSearchStore())
|
||||
|
||||
const groupItems = computed<IPasswordMenuItem[]>(() => {
|
||||
const items: IPasswordMenuItem[] = []
|
||||
const menuItems: IPasswordMenuItem[] = []
|
||||
|
||||
items.push(
|
||||
...currentGroupItems.value.groups.map<IPasswordMenuItem>((group) => ({
|
||||
color: group.color,
|
||||
icon: group.icon,
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
type: 'group',
|
||||
})),
|
||||
menuItems.push(
|
||||
...groups.value
|
||||
.filter((group) => {
|
||||
if (!search.value) return group.parentId == currentGroupId.value
|
||||
|
||||
return (
|
||||
group.name?.includes(search.value) ||
|
||||
group.description?.includes(search.value)
|
||||
)
|
||||
})
|
||||
.map<IPasswordMenuItem>((group) => ({
|
||||
color: group.color,
|
||||
icon: group.icon,
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
type: 'group',
|
||||
})),
|
||||
)
|
||||
|
||||
items.push(
|
||||
...currentGroupItems.value.items.map<IPasswordMenuItem>((item) => ({
|
||||
icon: item.icon,
|
||||
id: item.id,
|
||||
name: item.title,
|
||||
type: 'item',
|
||||
})),
|
||||
menuItems.push(
|
||||
...items.value
|
||||
.filter((item) => {
|
||||
if (!search.value)
|
||||
return item.haex_passwords_group_items.groupId == currentGroupId.value
|
||||
|
||||
return (
|
||||
item.haex_passwords_item_details.title?.includes(search.value) ||
|
||||
item.haex_passwords_item_details.note?.includes(search.value) ||
|
||||
item.haex_passwords_item_details.password?.includes(search.value) ||
|
||||
item.haex_passwords_item_details.tags?.includes(search.value) ||
|
||||
item.haex_passwords_item_details.url?.includes(search.value) ||
|
||||
item.haex_passwords_item_details.username?.includes(search.value)
|
||||
)
|
||||
})
|
||||
.map<IPasswordMenuItem>((item) => ({
|
||||
icon: item.haex_passwords_item_details.icon,
|
||||
id: item.haex_passwords_item_details.id,
|
||||
name: item.haex_passwords_item_details.title,
|
||||
type: 'item',
|
||||
})),
|
||||
)
|
||||
return items
|
||||
|
||||
return menuItems
|
||||
})
|
||||
|
||||
const { isVisible } = storeToRefs(useSidebarStore())
|
||||
@ -150,8 +187,7 @@ onKeyStroke('x', (event) => {
|
||||
}
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const { add } = useSnackbar()
|
||||
const { insertGroupItemsAsync } = usePasswordGroupStore()
|
||||
|
||||
const onPasteAsync = async () => {
|
||||
if (!selectedGroupItems.value?.length) return
|
||||
@ -190,7 +226,8 @@ onKeyStroke('a', (event) => {
|
||||
})
|
||||
|
||||
const { deleteAsync } = usePasswordItemStore()
|
||||
const { deleteGroupAsync, syncGroupItemsAsync } = usePasswordGroupStore()
|
||||
const { deleteGroupAsync } = usePasswordGroupStore()
|
||||
|
||||
const onDeleteAsync = async () => {
|
||||
for (const item of selectedItems.value) {
|
||||
if (item.type === 'group') {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<HaexPassItem
|
||||
:history="item.history"
|
||||
:read_only
|
||||
@close="onClose"
|
||||
@close="onClose()"
|
||||
@submit="onUpdateAsync"
|
||||
v-model:details="item.details"
|
||||
v-model:key-values-add="item.keyValuesAdd"
|
||||
@ -11,7 +11,7 @@
|
||||
v-model:key-values="item.keyValues"
|
||||
/>
|
||||
|
||||
<div
|
||||
<!-- <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']"
|
||||
>
|
||||
@ -85,27 +85,35 @@
|
||||
</UiButton>
|
||||
</UiTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<UiDialogConfirm
|
||||
<HaexPassMenuBottom
|
||||
: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-close-button
|
||||
show-delete-button
|
||||
>
|
||||
</HaexPassMenuBottom>
|
||||
|
||||
<HaexPassDialogDeleteItem
|
||||
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>
|
||||
</HaexPassDialogDeleteItem>
|
||||
|
||||
<UiDialogConfirm
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
:confirm-label="t('dialog.unsavedChanges.label')"
|
||||
:title="t('dialog.unsavedChanges.title')"
|
||||
<HaexPassDialogUnsavedChanges
|
||||
:has-changes="hasChanges"
|
||||
@abort="showUnsavedChangesDialog = false"
|
||||
@confirm="onConfirmIgnoreChanges"
|
||||
>
|
||||
{{ t('dialog.unsavedChanges.question') }}
|
||||
</UiDialogConfirm>
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -126,7 +134,6 @@ defineProps({
|
||||
withCopyButton: Boolean,
|
||||
})
|
||||
|
||||
const { isVisible } = storeToRefs(useSidebarStore())
|
||||
const read_only = ref(true)
|
||||
const showConfirmDeleteDialog = ref(false)
|
||||
const { t } = useI18n()
|
||||
@ -165,7 +172,6 @@ const { currentItem } = storeToRefs(usePasswordItemStore())
|
||||
watch(
|
||||
currentItem,
|
||||
() => {
|
||||
console.log('watch currentItem', currentItem.value)
|
||||
if (!currentItem.value) return
|
||||
item.details = JSON.parse(JSON.stringify(currentItem.value?.details))
|
||||
item.keyValues = JSON.parse(JSON.stringify(currentItem.value?.keyValues))
|
||||
@ -174,15 +180,14 @@ watch(
|
||||
item.keyValuesDelete = []
|
||||
item.originalDetails = JSON.stringify(currentItem.value?.details)
|
||||
item.originalKeyValues = JSON.stringify(currentItem.value?.keyValues)
|
||||
ignoreChanges.value = false
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const { add } = useSnackbar()
|
||||
const { deleteAsync, updateAsync } = usePasswordItemStore()
|
||||
const { syncGroupItemsAsync, trashId } = usePasswordGroupStore()
|
||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||
const { syncGroupItemsAsync } = usePasswordGroupStore()
|
||||
const { currentGroupId, inTrashGroup } = storeToRefs(usePasswordGroupStore())
|
||||
|
||||
const onUpdateAsync = async () => {
|
||||
try {
|
||||
@ -195,31 +200,29 @@ const onUpdateAsync = async () => {
|
||||
})
|
||||
if (newId) add({ type: 'success', text: t('success.update') })
|
||||
syncGroupItemsAsync(currentGroupId.value)
|
||||
ignoreChanges.value = true
|
||||
onClose()
|
||||
onClose(true)
|
||||
} catch (error) {
|
||||
add({ type: 'error', text: t('error.update') })
|
||||
}
|
||||
}
|
||||
|
||||
const onClose = () => {
|
||||
const onClose = (ignoreChanges?: boolean) => {
|
||||
if (showConfirmDeleteDialog.value || showUnsavedChangesDialog.value) return
|
||||
|
||||
if (hasChanges.value && !ignoreChanges.value)
|
||||
if (hasChanges.value && !ignoreChanges)
|
||||
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)
|
||||
await deleteAsync(item.details.id, inTrashGroup.value)
|
||||
showConfirmDeleteDialog.value = false
|
||||
add({ type: 'success', text: t('success.delete') })
|
||||
syncGroupItemsAsync(currentGroupId.value)
|
||||
onClose()
|
||||
await syncGroupItemsAsync(currentGroupId.value)
|
||||
onClose(true)
|
||||
} catch (errro) {
|
||||
add({
|
||||
type: 'error',
|
||||
@ -239,22 +242,14 @@ const hasChanges = computed(
|
||||
)
|
||||
|
||||
const showUnsavedChangesDialog = ref(false)
|
||||
const ignoreChanges = ref(false)
|
||||
|
||||
const onConfirmIgnoreChanges = () => {
|
||||
ignoreChanges.value = true
|
||||
showUnsavedChangesDialog.value = false
|
||||
onClose()
|
||||
onClose(true)
|
||||
}
|
||||
</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
|
||||
@ -266,21 +261,7 @@ de:
|
||||
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
|
||||
@ -291,14 +272,4 @@ en:
|
||||
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>
|
||||
|
||||
@ -9,7 +9,15 @@
|
||||
v-model:key-values-add="item.keyValuesAdd"
|
||||
/>
|
||||
|
||||
<div
|
||||
<HaexPassMenuBottom
|
||||
@close="onClose"
|
||||
@save="onCreateAsync"
|
||||
show-close-button
|
||||
show-save-button
|
||||
>
|
||||
</HaexPassMenuBottom>
|
||||
|
||||
<!-- <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']"
|
||||
>
|
||||
@ -35,7 +43,7 @@
|
||||
</UiButton>
|
||||
</UiTooltip>
|
||||
<div class="flex items-center justify-center flex-1"></div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user