fixed unsaved changes

This commit is contained in:
2025-06-20 11:52:45 +02:00
parent b5114ac6fb
commit 3c954ac715
8 changed files with 223 additions and 167 deletions

View File

@ -77,7 +77,7 @@ watch(
try {
const foundGroup = await readGroupAsync(currentGroupId.value)
if (foundGroup) {
original.value = JSON.stringify(foundGroup)
original.value = JSON.parse(JSON.stringify(foundGroup))
group.value = foundGroup
}
} catch (error) {
@ -86,22 +86,11 @@ watch(
},
{ 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 read_only = ref(false)
const hasChanges = computed(
() => JSON.stringify(group.value) !== original.value,
)
const { areItemsEqual } = usePasswordGroup()
const hasChanges = computed(() => !!!areItemsEqual(group.value, original.value))
const onClose = () => {
if (showConfirmDeleteDialog.value || showUnsavedChangesDialog.value) return
@ -121,7 +110,7 @@ const onSaveAsync = async () => {
ignoreChanges.value = true
await updateAsync(group.value)
await syncGroupItemsAsync(group.value.id)
await syncGroupItemsAsync()
add({ type: 'success', text: t('change.success') })
onClose()
} catch (error) {
@ -141,7 +130,7 @@ const onDeleteAsync = async () => {
try {
const parentId = group.value.parentId
await deleteGroupAsync(group.value.id, inTrashGroup.value)
await syncGroupItemsAsync(parentId)
await syncGroupItemsAsync()
showConfirmDeleteDialog.value = false
ignoreChanges.value = true
await navigateTo(

View File

@ -194,7 +194,7 @@ const onPasteAsync = async () => {
[...selectedGroupItems.value],
currentGroupId.value,
)
await syncGroupItemsAsync(currentGroupId.value)
await syncGroupItemsAsync()
selectedGroupItems.value = []
selectedItems.value.clear()
} catch (error) {
@ -235,7 +235,7 @@ const onDeleteAsync = async () => {
}
}
selectedItems.value.clear()
await syncGroupItemsAsync(currentGroupId.value)
await syncGroupItemsAsync()
}
const keys = useMagicKeys()
watch(keys.delete, async () => {

View File

@ -1,5 +1,11 @@
<template>
<div>
<div class="flex flex-col">
<p>
{{ item.originalDetails }}
</p>
{{ item.details }}
</div>
<HaexPassItem
:history="item.history"
:read_only
@ -11,83 +17,8 @@
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 flex-1">
<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 flex-1">
<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> -->
<HaexPassMenuBottom
:has-changes
:show-edit-button="read_only && !hasChanges"
:show-readonly-button="!read_only && !hasChanges"
:show-save-button="!read_only && hasChanges"
@ -119,6 +50,7 @@
</template>
<script setup lang="ts">
import { usePasswordGroup } from '~/components/haex/pass/group/composables'
import type {
SelectHaexPasswordsItemDetails,
SelectHaexPasswordsItemHistory,
@ -145,8 +77,8 @@ const item = reactive<{
keyValues: SelectHaexPasswordsItemKeyValues[]
keyValuesAdd: SelectHaexPasswordsItemKeyValues[]
keyValuesDelete: SelectHaexPasswordsItemKeyValues[]
originalDetails: string | null
originalKeyValues: string | null
originalDetails: SelectHaexPasswordsItemDetails | null
originalKeyValues: SelectHaexPasswordsItemKeyValues[] | null
}>({
details: {
id: '',
@ -164,7 +96,18 @@ const item = reactive<{
history: [],
keyValuesAdd: [],
keyValuesDelete: [],
originalDetails: null,
originalDetails: {
id: '',
createdAt: null,
icon: null,
note: null,
password: null,
tags: null,
title: null,
updateAt: null,
url: null,
username: null,
},
originalKeyValues: null,
})
@ -179,8 +122,12 @@ watch(
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)
item.originalDetails = JSON.parse(
JSON.stringify(currentItem.value?.details),
)
item.originalKeyValues = JSON.parse(
JSON.stringify(currentItem.value?.keyValues),
)
},
{ immediate: true },
)
@ -201,7 +148,7 @@ const onUpdateAsync = async () => {
keyValuesDelete: item.keyValuesDelete,
})
if (newId) add({ type: 'success', text: t('success.update') })
syncGroupItemsAsync(currentGroupId.value)
syncGroupItemsAsync()
ignoreChanges.value = true
onClose()
} catch (error) {
@ -224,7 +171,7 @@ const deleteItemAsync = async () => {
await deleteAsync(item.details.id, inTrashGroup.value)
showConfirmDeleteDialog.value = false
add({ type: 'success', text: t('success.delete') })
await syncGroupItemsAsync(currentGroupId.value)
await syncGroupItemsAsync()
onClose()
} catch (errro) {
add({
@ -234,11 +181,12 @@ const deleteItemAsync = async () => {
}
}
const { areItemsEqual } = usePasswordGroup()
const hasChanges = computed(
() =>
!!(
item.originalDetails !== JSON.stringify(item.details) ||
item.originalKeyValues !== JSON.stringify(item.keyValues) ||
!areItemsEqual(item.originalDetails, item.details) ||
!areItemsEqual(item.originalKeyValues, item.keyValues) ||
item.keyValuesAdd.length ||
item.keyValuesDelete.length
),

View File

@ -1,5 +1,6 @@
<template>
<div>
{{ currentGroup?.id }} {{ currentGroupId }}
<HaexPassItem
:default-icon="currentGroup?.icon"
:history="item.history"
@ -10,6 +11,7 @@
/>
<HaexPassMenuBottom
:has-changes
@close="onClose"
@save="onCreateAsync"
show-close-button
@ -17,33 +19,13 @@
>
</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']"
>
<div class="flex items-center justify-center flex-1">
<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 flex-1"></div>
</div> -->
<HaexPassDialogUnsavedChanges
:has-changes="hasChanges"
v-model:ignore-changes="ignoreChanges"
@abort="showUnsavedChangesDialog = false"
@confirm="onConfirmIgnoreChanges"
v-model:open="showUnsavedChangesDialog"
/>
</div>
</template>
@ -64,17 +46,14 @@ defineProps({
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
originalDetails: SelectHaexPasswordsItemDetails
originalKeyValuesAdd: []
}>({
details: {
id: '',
@ -90,13 +69,23 @@ const item = reactive<{
},
history: [],
keyValuesAdd: [],
keyValuesDelete: [],
originalDetails: null,
originalKeyValues: null,
originalDetails: {
id: '',
createdAt: null,
icon: null,
note: null,
password: null,
tags: null,
title: null,
updateAt: null,
url: null,
username: null,
},
originalKeyValuesAdd: [],
})
const { add } = useSnackbar()
const { currentGroup } = storeToRefs(usePasswordGroupStore())
const { currentGroup, currentGroupId } = storeToRefs(usePasswordGroupStore())
const { syncGroupItemsAsync } = usePasswordGroupStore()
const { addAsync } = usePasswordItemStore()
@ -107,9 +96,11 @@ const onCreateAsync = async () => {
item.keyValuesAdd,
currentGroup.value,
)
if (newId) {
ignoreChanges.value = true
add({ type: 'success', text: t('success') })
syncGroupItemsAsync(currentGroup.value?.id)
await syncGroupItemsAsync()
onClose()
}
} catch (error) {
@ -117,7 +108,32 @@ const onCreateAsync = async () => {
}
}
const onClose = () => useRouter().back()
const ignoreChanges = ref(false)
const onClose = () => {
if (showUnsavedChangesDialog.value) return
if (hasChanges.value && !ignoreChanges.value)
return (showUnsavedChangesDialog.value = true)
useRouter().back()
}
const { areItemsEqual } = usePasswordGroup()
const hasChanges = computed(
() =>
!!(
!areItemsEqual(item.originalDetails, item.details) ||
item.keyValuesAdd.length
),
)
const showUnsavedChangesDialog = ref(false)
const onConfirmIgnoreChanges = () => {
showUnsavedChangesDialog.value = false
ignoreChanges.value = true
onClose()
}
</script>
<i18n lang="yaml">