feat(manacore/web): add undo toasts for delete and tag removal

Extend toast system with action buttons and toastStore.undo() helper.
After deleting a task/event/contact or removing a tag, a toast with
"Rückgängig" button appears for 5 seconds. Clicking it restores the
item (clears deletedAt) or re-adds the tag.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-03 14:50:32 +02:00
parent a673a69972
commit 81716725f2
6 changed files with 92 additions and 18 deletions

View file

@ -11,6 +11,7 @@
import type { LocalEvent } from '../types';
import { useAllTags, getTagsByIds } from '$lib/stores/tags.svelte';
import LinkedItems from '$lib/components/links/LinkedItems.svelte';
import { toastStore } from '@manacore/shared-ui/toast';
let { navigate, goBack, params }: ViewProps = $props();
let eventId = $derived(params.eventId as string);
@ -34,10 +35,11 @@
async function removeTag(tagId: string) {
const current = event?.tagIds ?? [];
await eventsStore.updateTagIds(
eventId,
current.filter((id) => id !== tagId)
);
const removed = current.filter((id) => id !== tagId);
await eventsStore.updateTagIds(eventId, removed);
toastStore.undo('Tag entfernt', () => {
eventsStore.updateTagIds(eventId, current);
});
}
$effect(() => {
@ -84,8 +86,12 @@
}
async function deleteEvent() {
await eventsStore.deleteEvent(eventId);
const id = eventId;
await eventsStore.deleteEvent(id);
goBack();
toastStore.undo('Termin gelöscht', () => {
db.table('events').update(id, { deletedAt: undefined, updatedAt: new Date().toISOString() });
});
}
</script>

View file

@ -20,6 +20,7 @@
import type { LocalContact } from '../types';
import { useAllTags, getTagsByIds } from '$lib/stores/tags.svelte';
import LinkedItems from '$lib/components/links/LinkedItems.svelte';
import { toastStore } from '@manacore/shared-ui/toast';
let { navigate, goBack, params }: ViewProps = $props();
let contactId = $derived(params.contactId as string);
@ -49,10 +50,11 @@
async function removeTag(tagId: string) {
const current = contact?.tagIds ?? [];
await contactsStore.updateTagIds(
contactId,
current.filter((id) => id !== tagId)
);
const removed = current.filter((id) => id !== tagId);
await contactsStore.updateTagIds(contactId, removed);
toastStore.undo('Tag entfernt', () => {
contactsStore.updateTagIds(contactId, current);
});
}
$effect(() => {
@ -119,8 +121,15 @@
}
async function deleteContact() {
await contactsStore.deleteContact(contactId);
const id = contactId;
await contactsStore.deleteContact(id);
goBack();
toastStore.undo('Kontakt gelöscht', () => {
db.table('contacts').update(id, {
deletedAt: undefined,
updatedAt: new Date().toISOString(),
});
});
}
</script>

View file

@ -11,6 +11,7 @@
import type { LocalTask, TaskPriority } from '../types';
import { useAllTags, getTagsByIds } from '$lib/stores/tags.svelte';
import LinkedItems from '$lib/components/links/LinkedItems.svelte';
import { toastStore } from '@manacore/shared-ui/toast';
let { navigate, goBack, params }: ViewProps = $props();
let taskId = $derived(params.taskId as string);
@ -38,10 +39,11 @@
async function removeTag(tagId: string) {
const current = getTaskTagIds();
await tasksStore.updateLabels(
taskId,
current.filter((id) => id !== tagId)
);
const removed = current.filter((id) => id !== tagId);
await tasksStore.updateLabels(taskId, removed);
toastStore.undo('Tag entfernt', () => {
tasksStore.updateLabels(taskId, current);
});
}
$effect(() => {
@ -96,8 +98,12 @@
}
async function deleteTask() {
await tasksStore.deleteTask(taskId);
const id = taskId;
await tasksStore.deleteTask(id);
goBack();
toastStore.undo('Aufgabe gelöscht', () => {
db.table('tasks').update(id, { deletedAt: undefined, updatedAt: new Date().toISOString() });
});
}
const priorityLabels: Record<TaskPriority, string> = {