feat(modal): warn before closing with unsaved changes

This commit is contained in:
Ulas Kalayci
2026-04-26 19:03:38 +02:00
parent 798f8ca87a
commit ed0f8b2d57
27 changed files with 112 additions and 40 deletions
+2 -2
View File
@@ -607,7 +607,7 @@ function openBudgetModal({ mode, entry = null }) {
panel.querySelector('#bm-cancel').addEventListener('click', closeModal);
panel.querySelector('#bm-delete')?.addEventListener('click', async () => {
closeModal();
closeModal({ force: true });
await deleteEntry(entry.id);
});
@@ -642,7 +642,7 @@ function openBudgetModal({ mode, entry = null }) {
const sumRes = await api.get(`/budget/summary?month=${state.month}`);
state.summary = sumRes.data;
closeModal();
closeModal({ force: true });
renderBody();
window.oikos?.showToast(mode === 'create' ? t('budget.addedToast') : t('budget.savedToast'), 'success');
} catch (err) {
+2 -2
View File
@@ -867,7 +867,7 @@ function openEventModal({ mode, event = null, date = null, reminder = null }) {
panel.querySelector('#modal-cancel').addEventListener('click', closeModal);
panel.querySelector('#modal-delete')?.addEventListener('click', async () => {
closeModal();
closeModal({ force: true });
await deleteEvent(event.id);
});
@@ -1060,7 +1060,7 @@ async function saveEvent(overlay, mode, eventId, existingReminder = null) {
}
}
closeModal();
closeModal({ force: true });
renderView();
window.oikos?.showToast(mode === 'create' ? t('calendar.createdToast') : t('calendar.savedToast'), 'success');
} catch (err) {
+2 -2
View File
@@ -304,7 +304,7 @@ function openContactModal({ mode, contact = null }) {
panel.querySelector('#cm-cancel').addEventListener('click', closeModal);
panel.querySelector('#cm-delete')?.addEventListener('click', async () => {
closeModal();
closeModal({ force: true });
await deleteContact(contact.id);
});
@@ -336,7 +336,7 @@ function openContactModal({ mode, contact = null }) {
const idx = state.contacts.findIndex((c) => c.id === contact.id);
if (idx !== -1) state.contacts[idx] = res.data;
}
closeModal();
closeModal({ force: true });
renderList();
window.oikos?.showToast(mode === 'create' ? t('contacts.savedToast') : t('contacts.updatedToast'), 'success');
} catch (err) {
+3 -3
View File
@@ -637,7 +637,7 @@ function openCustomizeModal(currentConfig, onSave) {
saveBtn.disabled = true;
try {
await api.put('/preferences', { dashboard_widgets: draft });
closeModal();
closeModal({ force: true });
onSave(draft);
window.oikos?.showToast(t('dashboard.customizeSaved'), 'success', 1500);
} catch {
@@ -674,7 +674,7 @@ function openTaskQuickAction(taskId, taskTitle, rerender) {
panel.querySelector('[data-action="done"]').addEventListener('click', async () => {
try {
await api.patch(`/tasks/${taskId}/status`, { status: 'done' });
closeModal();
closeModal({ force: true });
window.oikos?.showToast(t('tasks.swipedDoneToast'), 'success');
rerender();
} catch (err) {
@@ -682,7 +682,7 @@ function openTaskQuickAction(taskId, taskTitle, rerender) {
}
});
panel.querySelector('[data-action="edit"]').addEventListener('click', () => {
closeModal();
closeModal({ force: true });
window.oikos.navigate(`/tasks?open=${taskId}`);
});
},
+4 -4
View File
@@ -707,7 +707,7 @@ function openMealModal(opts) {
if (res.data.transferred > 0) {
window.oikos?.showToast(res.data.transferred !== 1 ? t('meals.transferSuccessPlural', { count: res.data.transferred }) : t('meals.transferSuccess', { count: res.data.transferred }), 'success');
await loadWeek(state.currentWeek);
closeModal();
closeModal({ force: true });
renderWeekGrid();
} else {
window.oikos?.showToast(t('meals.transferAlreadyDone'), 'info');
@@ -843,8 +843,8 @@ function ingredientRowHTML(name, qty, id, category = DEFAULT_CATEGORY_NAME) {
`;
}
function closeModal() {
closeSharedModal();
function closeModal({ force = false } = {}) {
closeSharedModal({ force });
state.modal = null;
}
@@ -894,7 +894,7 @@ async function saveModal(overlay) {
await loadWeek(state.currentWeek);
}
closeModal();
closeModal({ force: true });
renderWeekGrid();
window.oikos?.showToast(mode === 'create' ? t('meals.addMealTitle') : t('meals.editMeal'), 'success');
} catch (err) {
+2 -2
View File
@@ -445,7 +445,7 @@ function openNoteModal({ mode, note = null }) {
if (idx !== -1) state.notes[idx] = res.data;
state.notes.sort((a, b) => b.pinned - a.pinned);
}
closeModal();
closeModal({ force: true });
renderGrid();
window.oikos?.showToast(mode === 'create' ? t('notes.createdToast') : t('notes.savedToast'), 'success');
} catch (err) {
@@ -476,7 +476,7 @@ async function togglePin(id) {
}
async function deleteNote(id) {
closeModal();
closeModal({ force: true });
const note = state.notes.find((n) => n.id === id);
state.notes = state.notes.filter((n) => n.id !== id);
renderGrid();
+3 -3
View File
@@ -326,8 +326,8 @@ function openRecipeModal(mode, recipe = null) {
});
}
function closeModal() {
closeSharedModal();
function closeModal({ force = false } = {}) {
closeSharedModal({ force });
}
async function saveRecipe(panel, mode, recipe) {
@@ -361,7 +361,7 @@ async function saveRecipe(panel, mode, recipe) {
if (idx >= 0) state.recipes[idx] = res.data;
}
closeModal();
closeModal({ force: true });
renderRecipeList();
window.oikos?.showToast(mode === 'create' ? t('recipes.created') : t('recipes.updated'), 'success');
} catch (err) {
+2 -2
View File
@@ -570,7 +570,7 @@ async function handleFormSubmit(e, container) {
}
btnSuccess(submitBtn, originalLabel);
setTimeout(() => closeModal(), 700);
setTimeout(() => closeModal({ force: true }), 700);
await loadTasks(container);
} catch (err) {
errorEl.textContent = err.message;
@@ -582,7 +582,7 @@ async function handleFormSubmit(e, container) {
}
async function handleDeleteTask(id, container) {
closeModal();
closeModal({ force: true });
const itemEl = container.querySelector(`[data-task-id="${id}"]`);
if (itemEl) itemEl.style.display = 'none';