Fix admin modals replaying old action results after create or edit

This commit is contained in:
NeilClack
2026-09-10 13:26:54 -05:00
parent f1d80279e9
commit e60d61e90a
10 changed files with 265 additions and 78 deletions
@@ -38,6 +38,18 @@
let lastCreateForm = $state<ActionResult>(null); let lastCreateForm = $state<ActionResult>(null);
let lastReplaceForm = $state<ActionResult>(null); let lastReplaceForm = $state<ActionResult>(null);
let createTimer: ReturnType<typeof setTimeout> | undefined;
let replaceTimer: ReturnType<typeof setTimeout> | undefined;
function clearTimer(timer: 'create' | 'replace') {
const current = timer === 'create' ? createTimer : replaceTimer;
if (current) {
clearTimeout(current);
if (timer === 'create') createTimer = undefined;
else replaceTimer = undefined;
}
}
$effect(() => { $effect(() => {
if (!form) return; if (!form) return;
@@ -48,9 +60,10 @@
email = ''; email = '';
createError = ''; createError = '';
createSuccess = 'License key created and emailed'; createSuccess = 'License key created and emailed';
setTimeout(() => { createTimer = setTimeout(() => {
createOpen = false; createOpen = false;
createSuccess = ''; createSuccess = '';
createTimer = undefined;
}, 1200); }, 1200);
} else { } else {
createSuccess = ''; createSuccess = '';
@@ -62,9 +75,10 @@
if (form.success) { if (form.success) {
replaceError = ''; replaceError = '';
replaceSuccess = 'License key replaced'; replaceSuccess = 'License key replaced';
setTimeout(() => { replaceTimer = setTimeout(() => {
replaceOpen = false; replaceOpen = false;
replaceSuccess = ''; replaceSuccess = '';
replaceTimer = undefined;
}, 1200); }, 1200);
} else { } else {
replaceSuccess = ''; replaceSuccess = '';
@@ -74,25 +88,30 @@
}); });
function openCreate() { function openCreate() {
lastCreateForm = null; clearTimer('create');
lastCreateForm = form;
email = '';
createError = ''; createError = '';
createSuccess = ''; createSuccess = '';
createOpen = true; createOpen = true;
} }
function openReplace(record: Record<string, unknown>) { function openReplace(record: Record<string, unknown>) {
clearTimer('replace');
replaceRecord = record; replaceRecord = record;
lastReplaceForm = null; lastReplaceForm = form;
replaceError = ''; replaceError = '';
replaceSuccess = ''; replaceSuccess = '';
replaceOpen = true; replaceOpen = true;
} }
function closeCreate() { function closeCreate() {
clearTimer('create');
createOpen = false; createOpen = false;
} }
function closeReplace() { function closeReplace() {
clearTimer('replace');
replaceOpen = false; replaceOpen = false;
replaceRecord = null; replaceRecord = null;
} }
@@ -26,15 +26,21 @@
let successMessage = $state(''); let successMessage = $state('');
let lastForm = $state<ActionResult>(null); let lastForm = $state<ActionResult>(null);
$effect(() => { let closeTimer: ReturnType<typeof setTimeout> | undefined;
const rec = editingRecord ?? {};
name = String(rec.name ?? ''); function clearCloseTimer() {
if (!editingRecord) { if (closeTimer) {
formError = ''; clearTimeout(closeTimer);
fieldErrors = {}; closeTimer = undefined;
successMessage = '';
} }
}); }
function resetModalState() {
clearCloseTimer();
formError = '';
fieldErrors = {};
successMessage = '';
}
$effect(() => { $effect(() => {
if (!form || form === lastForm) return; if (!form || form === lastForm) return;
@@ -44,9 +50,10 @@
formError = ''; formError = '';
fieldErrors = {}; fieldErrors = {};
successMessage = editingRecord ? 'Category updated' : 'Category created'; successMessage = editingRecord ? 'Category updated' : 'Category created';
setTimeout(() => { closeTimer = setTimeout(() => {
modalOpen = false; modalOpen = false;
successMessage = ''; successMessage = '';
closeTimer = undefined;
}, 1200); }, 1200);
} else { } else {
successMessage = ''; successMessage = '';
@@ -63,18 +70,23 @@
} }
function openCreate() { function openCreate() {
resetModalState();
editingRecord = null; editingRecord = null;
lastForm = null; name = '';
lastForm = form;
modalOpen = true; modalOpen = true;
} }
function openEdit(record: Record<string, unknown>) { function openEdit(record: Record<string, unknown>) {
resetModalState();
editingRecord = record; editingRecord = record;
lastForm = null; name = String(record.name ?? '');
lastForm = form;
modalOpen = true; modalOpen = true;
} }
function closeModal() { function closeModal() {
clearCloseTimer();
modalOpen = false; modalOpen = false;
editingRecord = null; editingRecord = null;
} }
@@ -26,15 +26,21 @@
let successMessage = $state(''); let successMessage = $state('');
let lastForm = $state<ActionResult>(null); let lastForm = $state<ActionResult>(null);
$effect(() => { let closeTimer: ReturnType<typeof setTimeout> | undefined;
const rec = editingRecord ?? {};
name = String(rec.name ?? ''); function clearCloseTimer() {
if (!editingRecord) { if (closeTimer) {
formError = ''; clearTimeout(closeTimer);
fieldErrors = {}; closeTimer = undefined;
successMessage = '';
} }
}); }
function resetModalState() {
clearCloseTimer();
formError = '';
fieldErrors = {};
successMessage = '';
}
$effect(() => { $effect(() => {
if (!form || form === lastForm) return; if (!form || form === lastForm) return;
@@ -44,9 +50,10 @@
formError = ''; formError = '';
fieldErrors = {}; fieldErrors = {};
successMessage = editingRecord ? 'Group updated' : 'Group created'; successMessage = editingRecord ? 'Group updated' : 'Group created';
setTimeout(() => { closeTimer = setTimeout(() => {
modalOpen = false; modalOpen = false;
successMessage = ''; successMessage = '';
closeTimer = undefined;
}, 1200); }, 1200);
} else { } else {
successMessage = ''; successMessage = '';
@@ -63,18 +70,23 @@
} }
function openCreate() { function openCreate() {
resetModalState();
editingRecord = null; editingRecord = null;
lastForm = null; name = '';
lastForm = form;
modalOpen = true; modalOpen = true;
} }
function openEdit(record: Record<string, unknown>) { function openEdit(record: Record<string, unknown>) {
resetModalState();
editingRecord = record; editingRecord = record;
lastForm = null; name = String(record.name ?? '');
lastForm = form;
modalOpen = true; modalOpen = true;
} }
function closeModal() { function closeModal() {
clearCloseTimer();
modalOpen = false; modalOpen = false;
editingRecord = null; editingRecord = null;
} }
@@ -53,19 +53,21 @@
let contentKey = $derived(editingRecord?.id ?? 'new'); let contentKey = $derived(editingRecord?.id ?? 'new');
$effect(() => { let closeTimer: ReturnType<typeof setTimeout> | undefined;
const record = editingRecord ?? {};
name = String(record.name ?? ''); function clearCloseTimer() {
solfeggioFrequencyId = String(record.solfeggioFrequencyId ?? ''); if (closeTimer) {
categoryId = String(record.categoryId ?? ''); clearTimeout(closeTimer);
groupId = String(record.groupId ?? ''); closeTimer = undefined;
content = typeof record.content === 'string' ? record.content : '';
if (!editingRecord) {
formError = '';
fieldErrors = {};
successMessage = '';
} }
}); }
function resetModalState() {
clearCloseTimer();
formError = '';
fieldErrors = {};
successMessage = '';
}
$effect(() => { $effect(() => {
if (!form || form === lastForm) return; if (!form || form === lastForm) return;
@@ -75,6 +77,11 @@
formError = ''; formError = '';
fieldErrors = {}; fieldErrors = {};
successMessage = editingRecord ? 'Proclamation updated' : 'Proclamation created'; successMessage = editingRecord ? 'Proclamation updated' : 'Proclamation created';
closeTimer = setTimeout(() => {
modalOpen = false;
successMessage = '';
closeTimer = undefined;
}, 1200);
} else { } else {
successMessage = ''; successMessage = '';
formError = form.error ?? ''; formError = form.error ?? '';
@@ -133,18 +140,31 @@
} }
function openCreate() { function openCreate() {
resetModalState();
editingRecord = null; editingRecord = null;
lastForm = null; name = '';
solfeggioFrequencyId = '';
categoryId = '';
groupId = '';
content = '';
lastForm = form;
modalOpen = true; modalOpen = true;
} }
function openEdit(record: Record<string, unknown>) { function openEdit(record: Record<string, unknown>) {
resetModalState();
editingRecord = record; editingRecord = record;
lastForm = null; name = String(record.name ?? '');
solfeggioFrequencyId = String(record.solfeggioFrequencyId ?? '');
categoryId = String(record.categoryId ?? '');
groupId = String(record.groupId ?? '');
content = typeof record.content === 'string' ? record.content : '';
lastForm = form;
modalOpen = true; modalOpen = true;
} }
function closeModal() { function closeModal() {
clearCloseTimer();
modalOpen = false; modalOpen = false;
editingRecord = null; editingRecord = null;
} }
@@ -25,7 +25,17 @@
let successMessage = $state(''); let successMessage = $state('');
let lastForm = $state<ActionResult>(null); let lastForm = $state<ActionResult>(null);
let closeTimer: ReturnType<typeof setTimeout> | undefined;
function clearCloseTimer() {
if (closeTimer) {
clearTimeout(closeTimer);
closeTimer = undefined;
}
}
$effect(() => { $effect(() => {
clearCloseTimer();
if (open) { if (open) {
formError = ''; formError = '';
fieldErrors = {}; fieldErrors = {};
@@ -37,11 +47,19 @@
if (!form || form === lastForm) return; if (!form || form === lastForm) return;
lastForm = form; lastForm = form;
// Consume results even while closed so a stale result is never
// replayed when the modal is next opened, but only show feedback
// while this modal is actually open.
if (!open) return;
if (form.success) { if (form.success) {
formError = ''; formError = '';
fieldErrors = {}; fieldErrors = {};
successMessage = 'Profile created'; successMessage = 'Profile created';
setTimeout(() => onClose?.(), 1200); closeTimer = setTimeout(() => {
closeTimer = undefined;
onClose?.();
}, 1200);
} else { } else {
successMessage = ''; successMessage = '';
formError = form.error ?? ''; formError = form.error ?? '';
@@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import { untrack } from 'svelte';
import { enhance } from '$app/forms'; import { enhance } from '$app/forms';
import AdminModal from '$lib/components/admin/AdminModal.svelte'; import AdminModal from '$lib/components/admin/AdminModal.svelte';
import ProfileFields from './ProfileFields.svelte'; import ProfileFields from './ProfileFields.svelte';
@@ -43,7 +44,9 @@
let addrShipping = $state(false); let addrShipping = $state(false);
let lastRecordId = $state<string | null>(null); let lastRecordId = $state<string | null>(null);
let lastForm = $state<ActionResult>(null); // Capture the form result as it is when this modal mounts so a stale
// result from a previous record is not replayed on open.
let lastForm = $state<ActionResult>(untrack(() => form ?? null));
function resetAddressFields() { function resetAddressFields() {
addrStreet = ''; addrStreet = '';
@@ -29,16 +29,21 @@
let successMessage = $state(''); let successMessage = $state('');
let lastForm = $state<ActionResult>(null); let lastForm = $state<ActionResult>(null);
$effect(() => { let closeTimer: ReturnType<typeof setTimeout> | undefined;
const rec = editingRecord ?? {};
name = String(rec.name ?? ''); function clearCloseTimer() {
value = String(rec.value ?? ''); if (closeTimer) {
if (!editingRecord) { clearTimeout(closeTimer);
formError = ''; closeTimer = undefined;
fieldErrors = {};
successMessage = '';
} }
}); }
function resetModalState() {
clearCloseTimer();
formError = '';
fieldErrors = {};
successMessage = '';
}
$effect(() => { $effect(() => {
if (!form || form === lastForm) return; if (!form || form === lastForm) return;
@@ -48,9 +53,10 @@
formError = ''; formError = '';
fieldErrors = {}; fieldErrors = {};
successMessage = editingRecord ? 'Frequency updated' : 'Frequency created'; successMessage = editingRecord ? 'Frequency updated' : 'Frequency created';
setTimeout(() => { closeTimer = setTimeout(() => {
modalOpen = false; modalOpen = false;
successMessage = ''; successMessage = '';
closeTimer = undefined;
}, 1200); }, 1200);
} else { } else {
successMessage = ''; successMessage = '';
@@ -60,18 +66,25 @@
}); });
function openCreate() { function openCreate() {
resetModalState();
editingRecord = null; editingRecord = null;
lastForm = null; name = '';
value = '';
lastForm = form;
modalOpen = true; modalOpen = true;
} }
function openEdit(record: Record<string, unknown>) { function openEdit(record: Record<string, unknown>) {
resetModalState();
editingRecord = record; editingRecord = record;
lastForm = null; name = String(record.name ?? '');
value = String(record.value ?? '');
lastForm = form;
modalOpen = true; modalOpen = true;
} }
function closeModal() { function closeModal() {
clearCloseTimer();
modalOpen = false; modalOpen = false;
editingRecord = null; editingRecord = null;
} }
@@ -28,15 +28,21 @@
let successMessage = $state(''); let successMessage = $state('');
let lastForm = $state<ActionResult>(null); let lastForm = $state<ActionResult>(null);
$effect(() => { let closeTimer: ReturnType<typeof setTimeout> | undefined;
const rec = editingRecord ?? {};
name = String(rec.name ?? ''); function clearCloseTimer() {
if (!editingRecord) { if (closeTimer) {
formError = ''; clearTimeout(closeTimer);
fieldErrors = {}; closeTimer = undefined;
successMessage = '';
} }
}); }
function resetModalState() {
clearCloseTimer();
formError = '';
fieldErrors = {};
successMessage = '';
}
$effect(() => { $effect(() => {
if (!form || form === lastForm) return; if (!form || form === lastForm) return;
@@ -46,9 +52,10 @@
formError = ''; formError = '';
fieldErrors = {}; fieldErrors = {};
successMessage = editingRecord ? 'Role updated' : 'Role created'; successMessage = editingRecord ? 'Role updated' : 'Role created';
setTimeout(() => { closeTimer = setTimeout(() => {
modalOpen = false; modalOpen = false;
successMessage = ''; successMessage = '';
closeTimer = undefined;
}, 1200); }, 1200);
} else { } else {
successMessage = ''; successMessage = '';
@@ -58,18 +65,23 @@
}); });
function openCreate() { function openCreate() {
resetModalState();
editingRecord = null; editingRecord = null;
lastForm = null; name = '';
lastForm = form;
modalOpen = true; modalOpen = true;
} }
function openEdit(record: Record<string, unknown>) { function openEdit(record: Record<string, unknown>) {
resetModalState();
editingRecord = record; editingRecord = record;
lastForm = null; name = String(record.name ?? '');
lastForm = form;
modalOpen = true; modalOpen = true;
} }
function closeModal() { function closeModal() {
clearCloseTimer();
modalOpen = false; modalOpen = false;
editingRecord = null; editingRecord = null;
} }
+27 -13
View File
@@ -46,17 +46,21 @@
let successMessage = $state(''); let successMessage = $state('');
let lastForm = $state<ActionResult>(null); let lastForm = $state<ActionResult>(null);
$effect(() => { let closeTimer: ReturnType<typeof setTimeout> | undefined;
const rec = editingRecord ?? {};
name = String(rec.name ?? ''); function clearCloseTimer() {
email = String(rec.email ?? ''); if (closeTimer) {
roleId = String(rec.roleId ?? ''); clearTimeout(closeTimer);
if (!editingRecord) { closeTimer = undefined;
formError = '';
fieldErrors = {};
successMessage = '';
} }
}); }
function resetModalState() {
clearCloseTimer();
formError = '';
fieldErrors = {};
successMessage = '';
}
$effect(() => { $effect(() => {
if (!form || form === lastForm) return; if (!form || form === lastForm) return;
@@ -66,9 +70,10 @@
formError = ''; formError = '';
fieldErrors = {}; fieldErrors = {};
successMessage = editingRecord ? 'User updated' : 'User created'; successMessage = editingRecord ? 'User updated' : 'User created';
setTimeout(() => { closeTimer = setTimeout(() => {
modalOpen = false; modalOpen = false;
successMessage = ''; successMessage = '';
closeTimer = undefined;
}, 1200); }, 1200);
} else { } else {
successMessage = ''; successMessage = '';
@@ -78,18 +83,27 @@
}); });
function openCreate() { function openCreate() {
resetModalState();
editingRecord = null; editingRecord = null;
lastForm = null; name = '';
email = '';
roleId = '';
lastForm = form;
modalOpen = true; modalOpen = true;
} }
function openEdit(record: Record<string, unknown>) { function openEdit(record: Record<string, unknown>) {
resetModalState();
editingRecord = record; editingRecord = record;
lastForm = null; name = String(record.name ?? '');
email = String(record.email ?? '');
roleId = String(record.roleId ?? '');
lastForm = form;
modalOpen = true; modalOpen = true;
} }
function closeModal() { function closeModal() {
clearCloseTimer();
modalOpen = false; modalOpen = false;
editingRecord = null; editingRecord = null;
} }
@@ -0,0 +1,64 @@
import { page } from 'vitest/browser';
import { describe, it, expect, vi } from 'vitest';
import { render } from 'vitest-browser-svelte';
import type { PageProps } from './$types';
vi.mock('$app/forms', () => ({
enhance: () => () => {}
}));
import Page from './+page.svelte';
const superAdmin = {
id: 'admin-1',
name: 'Super Admin',
roleId: 1,
roleName: 'SUPER'
};
function makeData(overrides: Partial<PageProps['data']> = {}): PageProps['data'] {
return {
records: [],
roleOptions: [{ id: 4, name: 'USER' }],
adminUser: superAdmin,
userRoleId: 1,
...overrides
};
}
async function clickButton(name: string) {
const loc = page.getByRole('button', { name });
const el = (await loc.element()) as HTMLElement;
el.click();
}
describe('users admin page', () => {
it('does not replay a stale success result when reopening the create modal', async () => {
render(Page, {
params: {},
data: makeData(),
form: { success: true, action: 'upsert' }
});
await clickButton('New User');
await expect.element(page.getByRole('button', { name: 'Create User' })).toBeInTheDocument();
expect(page.getByText('User created').elements()).toHaveLength(0);
});
it('does not replay a stale success result when opening the edit modal', async () => {
const record = { id: 'user-1', name: 'Jane Doe', email: 'jane@example.com', roleId: 4 };
render(Page, {
params: {},
data: makeData({ records: [record] }),
form: { success: true, action: 'upsert' }
});
const cell = (await page.getByRole('cell', { name: 'Jane Doe' }).element()) as HTMLElement;
cell.click();
await expect.element(page.getByRole('button', { name: 'Save User' })).toBeInTheDocument();
expect(page.getByText('User updated').elements()).toHaveLength(0);
});
});