mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:41:09 +02:00
feat(todo-web): auto-save task edits, remove save/cancel buttons
Changes in the expanded inline task editor are now saved automatically with a 500ms debounce. Removes the need to manually click "Speichern". The delete button remains for intentional destructive actions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ee3e815f1d
commit
49457c354a
2 changed files with 42 additions and 70 deletions
|
|
@ -69,6 +69,8 @@
|
|||
let isLoading = $state(false);
|
||||
let showDeleteConfirm = $state(false);
|
||||
let titleInputRef = $state<HTMLInputElement | null>(null);
|
||||
let autoSaveTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let isInitializing = $state(false);
|
||||
|
||||
// Focus title input when expanded
|
||||
$effect(() => {
|
||||
|
|
@ -81,6 +83,7 @@
|
|||
// Initialize form when expanded
|
||||
$effect(() => {
|
||||
if (isExpanded && task) {
|
||||
isInitializing = true;
|
||||
title = task.title || '';
|
||||
description = task.description || '';
|
||||
dueDate = task.dueDate ? format(new Date(task.dueDate), 'yyyy-MM-dd') : '';
|
||||
|
|
@ -103,9 +106,48 @@
|
|||
contactsStore.checkAvailability().then((available) => {
|
||||
contactsAvailable = available;
|
||||
});
|
||||
|
||||
// Allow a tick for all state to settle before enabling auto-save
|
||||
setTimeout(() => {
|
||||
isInitializing = false;
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-save: debounce changes and save automatically
|
||||
function scheduleAutoSave() {
|
||||
if (isInitializing || !isExpanded) return;
|
||||
if (autoSaveTimer) clearTimeout(autoSaveTimer);
|
||||
autoSaveTimer = setTimeout(() => {
|
||||
handleSave();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Watch all form fields for changes and trigger auto-save
|
||||
$effect(() => {
|
||||
// Read all reactive form fields to create dependencies
|
||||
void [
|
||||
title,
|
||||
description,
|
||||
dueDate,
|
||||
dueTime,
|
||||
startDate,
|
||||
priority,
|
||||
status,
|
||||
projectId,
|
||||
selectedLabelIds,
|
||||
subtasks,
|
||||
recurrenceRule,
|
||||
notes,
|
||||
storyPoints,
|
||||
effectiveDuration,
|
||||
funRating,
|
||||
assignee,
|
||||
involvedContacts,
|
||||
];
|
||||
scheduleAutoSave();
|
||||
});
|
||||
|
||||
// Animation state for completing
|
||||
let isAnimatingComplete = $state(false);
|
||||
|
||||
|
|
@ -140,9 +182,6 @@
|
|||
if (!isExpanded) return;
|
||||
if (e.key === 'Escape') {
|
||||
onCollapse?.();
|
||||
} else if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
handleSave();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -566,23 +605,6 @@
|
|||
>
|
||||
{showDeleteConfirm ? 'Wirklich löschen?' : 'Löschen'}
|
||||
</button>
|
||||
<div class="actions-right">
|
||||
<button type="button" class="btn btn-secondary" onclick={onCollapse} disabled={isLoading}>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
onclick={handleSave}
|
||||
disabled={isLoading || !title.trim()}
|
||||
>
|
||||
{#if isLoading}
|
||||
<div class="spinner"></div>
|
||||
{:else}
|
||||
Speichern
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -1110,11 +1132,6 @@
|
|||
border-top-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.actions-right {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -1134,33 +1151,6 @@
|
|||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #8b5cf6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: #7c3aed;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
:global(.dark) .btn-secondary {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.btn-secondary:hover:not(:disabled) {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
:global(.dark) .btn-secondary:hover:not(:disabled) {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: #ef4444;
|
||||
|
|
@ -1170,19 +1160,4 @@
|
|||
background: #ef4444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
border: 2px solid transparent;
|
||||
border-top-color: white;
|
||||
border-radius: 9999px;
|
||||
animation: spin 0.6s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -51,9 +51,6 @@
|
|||
if (data.labelIds !== undefined) {
|
||||
await tasksStore.updateLabels(taskId, data.labelIds);
|
||||
}
|
||||
|
||||
// Collapse after save
|
||||
expandedTaskId = null;
|
||||
} catch (error) {
|
||||
console.error('Failed to save task:', error);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue