From af03e08dadd1d17c952033a16cac2c71198f74d8 Mon Sep 17 00:00:00 2001 From: ThomasSarlin Date: Mon, 9 Mar 2026 08:27:26 +0100 Subject: [PATCH 01/10] Add helper method to centralize the form submit functionality --- static/js/http.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/static/js/http.js b/static/js/http.js index 1d4bc47bb6..824eb5a9ee 100644 --- a/static/js/http.js +++ b/static/js/http.js @@ -1,3 +1,5 @@ +import * as DOM from './dom.js' + function request (method, path, options = {}) { const body = options.body const headers = options.headers || new window.Headers() @@ -66,8 +68,21 @@ function noencode (v) { return new NoUrlEscapeString(v) } +function submitForm (form, method, url, options = {}) { + const { body, table, toast, onSuccess } = options + return request(method, url, { body }) + .then(res => { + if (res?.is_error) return + if (table) table.reload() + if (toast) DOM.toast(toast) + if (onSuccess) onSuccess(res) + form.reset() + }) +} + export { request, + submitForm, url, noencode } From 4fef6e5aed758ef44ef1b95621f45247bcc82097 Mon Sep 17 00:00:00 2001 From: ThomasSarlin Date: Mon, 9 Mar 2026 08:34:50 +0100 Subject: [PATCH 02/10] Make it async instead of on Success --- static/js/http.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/static/js/http.js b/static/js/http.js index 824eb5a9ee..3f177c9125 100644 --- a/static/js/http.js +++ b/static/js/http.js @@ -68,14 +68,13 @@ function noencode (v) { return new NoUrlEscapeString(v) } -function submitForm (form, method, url, options = {}) { - const { body, table, toast, onSuccess } = options +async function submitForm (form, method, url, options = {}) { + const { body, table, toast } = options return request(method, url, { body }) .then(res => { if (res?.is_error) return if (table) table.reload() if (toast) DOM.toast(toast) - if (onSuccess) onSuccess(res) form.reset() }) } From 22fc7d38ef29280f1a861f14ec1032e6fd6d112a Mon Sep 17 00:00:00 2001 From: ThomasSarlin Date: Mon, 9 Mar 2026 08:35:07 +0100 Subject: [PATCH 03/10] Implement new formSubmit http helpers to forms --- static/js/exchange.js | 7 +------ static/js/exchanges.js | 11 +++++------ static/js/federation.js | 11 +++++------ static/js/policies.js | 6 +----- static/js/queue.js | 30 +++++++++++++----------------- static/js/queues.js | 14 +++++++------- static/js/shovels.js | 11 +++++------ static/js/stream.js | 12 +++++------- static/js/user.js | 16 ++++++---------- static/js/users.js | 11 +++++------ static/js/vhost.js | 6 +----- static/js/vhosts.js | 6 +----- 12 files changed, 55 insertions(+), 86 deletions(-) diff --git a/static/js/exchange.js b/static/js/exchange.js index 9f7205a21c..9098207983 100644 --- a/static/js/exchange.js +++ b/static/js/exchange.js @@ -102,12 +102,7 @@ document.querySelector('#addBinding').addEventListener('submit', function (evt) routing_key: data.get('routing_key').trim(), arguments: args } - HTTP.request('POST', url, { body }) - .then(res => { - if (res && res.is_error) return - bindingsTable.reload() - evt.target.reset() - }) + HTTP.submitForm(evt.target, 'POST', url, { body, table: bindingsTable }) .catch(e => { if (e.status === 404) { const type = t === 'q' ? 'Queue' : 'Exchange' diff --git a/static/js/exchanges.js b/static/js/exchanges.js index 4504e0150d..9d76b784cc 100644 --- a/static/js/exchanges.js +++ b/static/js/exchanges.js @@ -94,12 +94,11 @@ document.querySelector('#addExchange').addEventListener('submit', function (evt) type: data.get('type'), arguments: DOM.parseJSON(data.get('arguments')) } - HTTP.request('PUT', url, { body }) - .then(() => { - exchangeTable.reload() - DOM.toast('Exchange ' + exchange + ' created') - evt.target.reset() - }) + HTTP.submitForm(evt.target, 'PUT', url, { + body, + table: exchangeTable, + toast: 'Exchange ' + exchange + ' created' + }) }) document.querySelector('#dataTags').addEventListener('click', e => { diff --git a/static/js/federation.js b/static/js/federation.js index b73f99c1ef..7bf5061d2f 100644 --- a/static/js/federation.js +++ b/static/js/federation.js @@ -84,10 +84,9 @@ document.querySelector('#createUpstream').addEventListener('submit', function (e 'consumer-tag': data.get('consumer-tag') } } - HTTP.request('PUT', url, { body }) - .then(() => { - upstreamsTable.reload() - evt.target.reset() - DOM.toast(`Upstream ${name} saved`) - }) + HTTP.submitForm(evt.target, 'PUT', url, { + body, + table: upstreamsTable, + toast: `Upstream ${name} saved` + }) }) diff --git a/static/js/policies.js b/static/js/policies.js index b534e0e618..3f7477ddd4 100644 --- a/static/js/policies.js +++ b/static/js/policies.js @@ -65,11 +65,7 @@ document.querySelector('#createPolicy').addEventListener('submit', function (evt 'apply-to': data.get('apply-to'), priority: parseInt(data.get('priority')) } - HTTP.request('PUT', url, { body }) - .then(() => { - policiesTable.reload() - evt.target.reset() - }) + HTTP.submitForm(evt.target, 'PUT', url, { body, table: policiesTable }) }) document.querySelector('#dataTags').addEventListener('click', e => { Helpers.argumentHelperJSON('createPolicy', 'definition', e) diff --git a/static/js/queue.js b/static/js/queue.js index 46d2b63f22..547a0caead 100644 --- a/static/js/queue.js +++ b/static/js/queue.js @@ -200,18 +200,15 @@ document.querySelector('#addBinding').addEventListener('submit', function (evt) routing_key: data.get('routing_key').trim(), arguments: args } - HTTP.request('POST', url, { body }) - .then(res => { - if (res && res.is_error) return - bindingsTable.reload() - evt.target.reset() - DOM.toast('Exchange ' + e + ' bound to queue') - }) - .catch(err => { - if (err.status === 404) { - DOM.toast.error(`Exchange '${e}' does not exist and needs to be created first.`) - } - }) + HTTP.submitForm(evt.target, 'POST', url, { + body, + table: bindingsTable, + toast: 'Exchange ' + e + ' bound to queue' + }).catch(err => { + if (err.status === 404) { + DOM.toast.error(`Exchange '${e}' does not exist and needs to be created first.`) + } + }) }) document.querySelector('#publishMessage').addEventListener('submit', function (evt) { @@ -298,11 +295,10 @@ document.querySelector('#moveMessages').addEventListener('submit', function (evt 'src-delete-after': 'queue-length' } } - HTTP.request('PUT', url, { body }) - .then(() => { - evt.target.reset() - DOM.toast('Moving messages to ' + dest) - }) + HTTP.submitForm(evt.target, 'PUT', url, { + body, + toast: 'Moving messages to ' + dest + }) }) document.querySelector('#purgeQueue').addEventListener('submit', function (evt) { diff --git a/static/js/queues.js b/static/js/queues.js index a226bc35d0..be699511ec 100644 --- a/static/js/queues.js +++ b/static/js/queues.js @@ -164,14 +164,14 @@ document.querySelector('#declare').addEventListener('submit', function (evt) { auto_delete: data.get('auto_delete') === '1', arguments: DOM.parseJSON(data.get('arguments')) } - HTTP.request('PUT', url, { body }) - .then((response) => { - if (response?.is_error) { return } - queuesTable.reload() - evt.target.reset() + + HTTP.submitForm(evt.target, 'PUT', url, { + body, + table: queuesTable, + toast: 'Queue ' + queue + ' created' + }).then(function() { evt.target.querySelector('select[name="vhost"]').value = decodeURIComponent(vhost) // Keep selected vhost selected - DOM.toast('Queue ' + queue + ' created') - }) + }) }) queuesTable.on('updated', _ => { const checked = document.querySelectorAll('input[data-name]:checked') diff --git a/static/js/shovels.js b/static/js/shovels.js index b6998debd5..55cca4ef8d 100644 --- a/static/js/shovels.js +++ b/static/js/shovels.js @@ -210,12 +210,11 @@ document.querySelector('#createShovel').addEventListener('submit', function (evt } else { body.value['dest-exchange'] = data.get('dest-endpoint') } - HTTP.request('PUT', url, { body }) - .then(() => { - dataSource.reload() - evt.target.reset() - DOM.toast(`Shovel ${name} saved`) - }) + HTTP.submitForm(evt.target, 'PUT', url, { + body, + table: dataSource, + toast: `Shovel ${name} saved` + }) }) // function updateAutocomplete (e, id) { diff --git a/static/js/stream.js b/static/js/stream.js index 7428d19993..5822b021e3 100644 --- a/static/js/stream.js +++ b/static/js/stream.js @@ -178,13 +178,11 @@ document.querySelector('#addBinding').addEventListener('submit', function (evt) routing_key: data.get('routing_key').trim(), arguments: args } - HTTP.request('POST', url, { body }) - .then(res => { - if (res && res.is_error) return - bindingsTable.reload() - evt.target.reset() - DOM.toast('Exchange ' + e + ' bound to queue') - }) + HTTP.submitForm(evt.target, 'POST', url, { + body, + table: bindingsTable, + toast: 'Exchange ' + e + ' bound to queue' + }) }) document.querySelector('#publishMessage').addEventListener('submit', function (evt) { diff --git a/static/js/user.js b/static/js/user.js index 2c71442072..9510f4f61e 100644 --- a/static/js/user.js +++ b/static/js/user.js @@ -67,11 +67,7 @@ document.querySelector('#setPermission').addEventListener('submit', function (ev write: data.get('write'), read: data.get('read') } - HTTP.request('PUT', url, { body }) - .then(() => { - permissionsTable.reload() - evt.target.reset() - }) + HTTP.submitForm(evt.target, 'PUT', url, { body, table: permissionsTable }) }) document.querySelector('[name=remove_password]').addEventListener('change', function () { @@ -97,14 +93,14 @@ document.querySelector('#updateUser').addEventListener('submit', function (evt) } else if (data.get('password') !== '') { body.password = data.get('password') } - HTTP.request('PUT', url, { body }) - .then(() => { + HTTP.submitForm(evt.target, 'PUT', url, { + body, + toast: 'User updated' + }).then(function() { updateUser() - DOM.toast('User updated') - evt.target.reset() pwd.disabled = false pwd.required = true - }) + }) }) document.querySelector('#dataTags').addEventListener('click', e => { diff --git a/static/js/users.js b/static/js/users.js index 45e7b2f5b5..141dac4120 100644 --- a/static/js/users.js +++ b/static/js/users.js @@ -50,12 +50,11 @@ document.querySelector('#createUser').addEventListener('submit', function (evt) if (data.get('password') !== '') { body.password = data.get('password') } - HTTP.request('PUT', url, { body }) - .then(() => { - usersTable.reload() - DOM.toast(toastText) - evt.target.reset() - }) + HTTP.submitForm(evt.target, 'PUT', url, { + body, + table: usersTable, + toast: toastText + }) }) document.querySelector('#dataTags').addEventListener('click', e => { diff --git a/static/js/vhost.js b/static/js/vhost.js index 5f71b72b5c..3cd29e97c9 100644 --- a/static/js/vhost.js +++ b/static/js/vhost.js @@ -87,11 +87,7 @@ document.querySelector('#setPermission').addEventListener('submit', function (ev write: data.get('write'), read: data.get('read') } - HTTP.request('PUT', url, { body }) - .then(() => { - permissionsTable.reload() - evt.target.reset() - }) + HTTP.submitForm(evt.target, 'PUT', url, { body, table: permissionsTable }) }) document.forms.setLimits.addEventListener('submit', function (evt) { diff --git a/static/js/vhosts.js b/static/js/vhosts.js index 35620cf697..73521bdedc 100644 --- a/static/js/vhosts.js +++ b/static/js/vhosts.js @@ -35,9 +35,5 @@ document.querySelector('#createVhost').addEventListener('submit', function (evt) const data = new window.FormData(this) const name = data.get('name').trim() const url = HTTP.url`api/vhosts/${name}` - HTTP.request('PUT', url) - .then(() => { - vhostTable.reload() - evt.target.reset() - }) + HTTP.submitForm(evt.target, 'PUT', url, { table: vhostTable }) }) From 65c45ccedaa93284b26917da2184479aa3febbc5 Mon Sep 17 00:00:00 2001 From: ThomasSarlin Date: Mon, 9 Mar 2026 08:38:12 +0100 Subject: [PATCH 04/10] Add playwright specs. Co-Authored-By: Claude --- spec/frontend/submit_form.spec.js | 94 +++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 spec/frontend/submit_form.spec.js diff --git a/spec/frontend/submit_form.spec.js b/spec/frontend/submit_form.spec.js new file mode 100644 index 0000000000..8117ae31f5 --- /dev/null +++ b/spec/frontend/submit_form.spec.js @@ -0,0 +1,94 @@ +import * as helpers from './helpers.js' +import { test, expect } from './fixtures.js' + +test.describe('submitForm', _ => { + test.describe('exchanges form', _ => { + test.beforeEach(async ({ page, apimap }) => { + const exchangeTypesLoaded = apimap.get('/api/exchanges', []) + const overviewLoaded = apimap.get('/api/overview', { exchange_types: [{ name: 'topic' }, { name: 'direct' }] }) + await page.goto('/exchanges') + await overviewLoaded + }) + + test('resets form on successful submit', async ({ page }) => { + const exchangeName = 'test-exchange' + const putRequest = helpers.waitForPathRequest(page, `/api/exchanges/bar/${exchangeName}`, { method: 'PUT' }) + + const form = page.locator('#addExchange') + await form.getByLabel('Name', { exact: true }).fill(exchangeName) + await form.getByRole('button', { name: 'Add exchange' }).click() + + await expect(putRequest).toBeRequested() + await expect(form.getByLabel('Name', { exact: true })).toHaveValue('') + }) + + test('does not reset form on error response', async ({ page }) => { + const exchangeName = 'test-exchange' + // Intercept the PUT and return a 403 error + await page.route(url => url.pathname === `/api/exchanges/bar/${exchangeName}`, async route => { + if (route.request().method() === 'PUT') { + await route.fulfill({ + status: 403, + contentType: 'application/json', + body: JSON.stringify({ error: 'access_refused', reason: 'No permission' }) + }) + } else { + await route.fallback() + } + }) + + // Dismiss the alert that standardErrorHandler will show + page.on('dialog', dialog => dialog.dismiss()) + + const form = page.locator('#addExchange') + await form.getByLabel('Name', { exact: true }).fill(exchangeName) + await form.getByRole('button', { name: 'Add exchange' }).click() + + // Form should still have the value since request failed + await expect(form.getByLabel('Name', { exact: true })).toHaveValue(exchangeName) + }) + }) + + test.describe('queues form', _ => { + test.beforeEach(async ({ page, apimap }) => { + const queuesLoaded = apimap.get('/api/queues', []) + await page.goto('/queues') + await queuesLoaded + }) + + test('preserves vhost selection after successful submit', async ({ page }) => { + const queueName = 'test-queue' + const putRequest = helpers.waitForPathRequest(page, `/api/queues/bar/${queueName}`, { method: 'PUT' }) + + const form = page.locator('#declare') + await form.getByLabel('Name', { exact: true }).fill(queueName) + await form.getByRole('button', { name: 'Add queue' }).click() + + await expect(putRequest).toBeRequested() + await expect(form.locator('select[name="vhost"]')).toHaveValue('bar') + }) + + test('does not reset form on error response', async ({ page }) => { + const queueName = 'test-queue' + await page.route(url => url.pathname === `/api/queues/bar/${queueName}`, async route => { + if (route.request().method() === 'PUT') { + await route.fulfill({ + status: 403, + contentType: 'application/json', + body: JSON.stringify({ error: 'access_refused', reason: 'No permission' }) + }) + } else { + await route.fallback() + } + }) + + page.on('dialog', dialog => dialog.dismiss()) + + const form = page.locator('#declare') + await form.getByLabel('Name', { exact: true }).fill(queueName) + await form.getByRole('button', { name: 'Add queue' }).click() + + await expect(form.getByLabel('Name', { exact: true })).toHaveValue(queueName) + }) + }) +}) From 6eb0ffe882d03ee17621e9311805c44649e9aeff Mon Sep 17 00:00:00 2001 From: ThomasSarlin Date: Tue, 10 Mar 2026 08:08:36 +0100 Subject: [PATCH 05/10] lint-fix --- static/js/queues.js | 4 ++-- static/js/user.js | 8 ++++---- static/js/users.js | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/static/js/queues.js b/static/js/queues.js index be699511ec..a3342fa17b 100644 --- a/static/js/queues.js +++ b/static/js/queues.js @@ -169,8 +169,8 @@ document.querySelector('#declare').addEventListener('submit', function (evt) { body, table: queuesTable, toast: 'Queue ' + queue + ' created' - }).then(function() { - evt.target.querySelector('select[name="vhost"]').value = decodeURIComponent(vhost) // Keep selected vhost selected + }).then(function () { + evt.target.querySelector('select[name="vhost"]').value = decodeURIComponent(vhost) // Keep selected vhost selected }) }) queuesTable.on('updated', _ => { diff --git a/static/js/user.js b/static/js/user.js index 9510f4f61e..01af049d83 100644 --- a/static/js/user.js +++ b/static/js/user.js @@ -96,10 +96,10 @@ document.querySelector('#updateUser').addEventListener('submit', function (evt) HTTP.submitForm(evt.target, 'PUT', url, { body, toast: 'User updated' - }).then(function() { - updateUser() - pwd.disabled = false - pwd.required = true + }).then(function () { + updateUser() + pwd.disabled = false + pwd.required = true }) }) diff --git a/static/js/users.js b/static/js/users.js index 141dac4120..35aa89a216 100644 --- a/static/js/users.js +++ b/static/js/users.js @@ -1,7 +1,6 @@ import * as HTTP from './http.js' import * as Helpers from './helpers.js' import * as Table from './table.js' -import * as DOM from './dom.js' let usersTable = null HTTP.request('GET', 'api/permissions').then(permissions => { From 3aa300fc1bbabc5c5419c848fd4b552343485aa9 Mon Sep 17 00:00:00 2001 From: ThomasSarlin Date: Tue, 10 Mar 2026 08:24:12 +0100 Subject: [PATCH 06/10] Fix error with changed .then statements when there are errors --- static/js/http.js | 3 ++- static/js/queues.js | 7 ++++--- static/js/user.js | 11 ++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/static/js/http.js b/static/js/http.js index 3f177c9125..ce15ffccf1 100644 --- a/static/js/http.js +++ b/static/js/http.js @@ -69,13 +69,14 @@ function noencode (v) { } async function submitForm (form, method, url, options = {}) { - const { body, table, toast } = options + const { body, table, toast, callback } = options return request(method, url, { body }) .then(res => { if (res?.is_error) return if (table) table.reload() if (toast) DOM.toast(toast) form.reset() + if (callback) callback() }) } diff --git a/static/js/queues.js b/static/js/queues.js index a3342fa17b..ed82e6509a 100644 --- a/static/js/queues.js +++ b/static/js/queues.js @@ -168,9 +168,10 @@ document.querySelector('#declare').addEventListener('submit', function (evt) { HTTP.submitForm(evt.target, 'PUT', url, { body, table: queuesTable, - toast: 'Queue ' + queue + ' created' - }).then(function () { - evt.target.querySelector('select[name="vhost"]').value = decodeURIComponent(vhost) // Keep selected vhost selected + toast: 'Queue ' + queue + ' created', + callback: function () { + evt.target.querySelector('select[name="vhost"]').value = decodeURIComponent(vhost) // Keep selected vhost selected + } }) }) queuesTable.on('updated', _ => { diff --git a/static/js/user.js b/static/js/user.js index 01af049d83..562afb9be0 100644 --- a/static/js/user.js +++ b/static/js/user.js @@ -95,11 +95,12 @@ document.querySelector('#updateUser').addEventListener('submit', function (evt) } HTTP.submitForm(evt.target, 'PUT', url, { body, - toast: 'User updated' - }).then(function () { - updateUser() - pwd.disabled = false - pwd.required = true + toast: 'User updated', + callback: function () { + updateUser() + pwd.disabled = false + pwd.required = true + } }) }) From 2a5fe6327900d99b901ed73bfea9ee996ffc3e4f Mon Sep 17 00:00:00 2001 From: ThomasSarlin Date: Fri, 13 Mar 2026 16:26:25 +0100 Subject: [PATCH 07/10] chain promise and toast at origin --- static/js/exchanges.js | 5 +++-- static/js/federation.js | 5 +++-- static/js/http.js | 6 +----- static/js/queue.js | 10 ++++++---- static/js/queues.js | 10 +++++----- static/js/shovels.js | 5 +++-- static/js/stream.js | 5 +++-- static/js/user.js | 11 +++++------ static/js/users.js | 5 +++-- 9 files changed, 32 insertions(+), 30 deletions(-) diff --git a/static/js/exchanges.js b/static/js/exchanges.js index 9d76b784cc..002b2fc403 100644 --- a/static/js/exchanges.js +++ b/static/js/exchanges.js @@ -96,8 +96,9 @@ document.querySelector('#addExchange').addEventListener('submit', function (evt) } HTTP.submitForm(evt.target, 'PUT', url, { body, - table: exchangeTable, - toast: 'Exchange ' + exchange + ' created' + table: exchangeTable + }).then(() => { + DOM.toast(`Exhange ${exchange} created`) }) }) diff --git a/static/js/federation.js b/static/js/federation.js index 7bf5061d2f..95fd0942fd 100644 --- a/static/js/federation.js +++ b/static/js/federation.js @@ -86,7 +86,8 @@ document.querySelector('#createUpstream').addEventListener('submit', function (e } HTTP.submitForm(evt.target, 'PUT', url, { body, - table: upstreamsTable, - toast: `Upstream ${name} saved` + table: upstreamsTable + }).then(()=> { + DOM.toast(`Upstream ${name} saved`) }) }) diff --git a/static/js/http.js b/static/js/http.js index ce15ffccf1..38c3f14187 100644 --- a/static/js/http.js +++ b/static/js/http.js @@ -1,5 +1,3 @@ -import * as DOM from './dom.js' - function request (method, path, options = {}) { const body = options.body const headers = options.headers || new window.Headers() @@ -69,14 +67,12 @@ function noencode (v) { } async function submitForm (form, method, url, options = {}) { - const { body, table, toast, callback } = options + const { body, table } = options return request(method, url, { body }) .then(res => { if (res?.is_error) return if (table) table.reload() - if (toast) DOM.toast(toast) form.reset() - if (callback) callback() }) } diff --git a/static/js/queue.js b/static/js/queue.js index 547a0caead..e7ec25ff0b 100644 --- a/static/js/queue.js +++ b/static/js/queue.js @@ -202,8 +202,9 @@ document.querySelector('#addBinding').addEventListener('submit', function (evt) } HTTP.submitForm(evt.target, 'POST', url, { body, - table: bindingsTable, - toast: 'Exchange ' + e + ' bound to queue' + table: bindingsTable + }).then(() => { + DOM.toast(`Exchange ${e} bound to queue`) }).catch(err => { if (err.status === 404) { DOM.toast.error(`Exchange '${e}' does not exist and needs to be created first.`) @@ -296,8 +297,9 @@ document.querySelector('#moveMessages').addEventListener('submit', function (evt } } HTTP.submitForm(evt.target, 'PUT', url, { - body, - toast: 'Moving messages to ' + dest + body + }).then(() => { + DOM.toast(`Moving messages to ${dest}`) }) }) diff --git a/static/js/queues.js b/static/js/queues.js index ed82e6509a..152af3ee5a 100644 --- a/static/js/queues.js +++ b/static/js/queues.js @@ -167,13 +167,13 @@ document.querySelector('#declare').addEventListener('submit', function (evt) { HTTP.submitForm(evt.target, 'PUT', url, { body, - table: queuesTable, - toast: 'Queue ' + queue + ' created', - callback: function () { - evt.target.querySelector('select[name="vhost"]').value = decodeURIComponent(vhost) // Keep selected vhost selected - } + table: queuesTable + }).then(() => { + DOM.toast(`Queue ${queue} created`) + evt.target.querySelector('select[name="vhost"]').value = decodeURIComponent(vhost) // Keep selected vhost selected }) }) + queuesTable.on('updated', _ => { const checked = document.querySelectorAll('input[data-name]:checked') document.getElementById('multi-queue-count').textContent = checked.length diff --git a/static/js/shovels.js b/static/js/shovels.js index 55cca4ef8d..a11572b091 100644 --- a/static/js/shovels.js +++ b/static/js/shovels.js @@ -212,8 +212,9 @@ document.querySelector('#createShovel').addEventListener('submit', function (evt } HTTP.submitForm(evt.target, 'PUT', url, { body, - table: dataSource, - toast: `Shovel ${name} saved` + table: dataSource + }).then(()=> { + DOM.toast(`Shovel ${name} saved`) }) }) diff --git a/static/js/stream.js b/static/js/stream.js index 5822b021e3..9b3f3684e8 100644 --- a/static/js/stream.js +++ b/static/js/stream.js @@ -180,8 +180,9 @@ document.querySelector('#addBinding').addEventListener('submit', function (evt) } HTTP.submitForm(evt.target, 'POST', url, { body, - table: bindingsTable, - toast: 'Exchange ' + e + ' bound to queue' + table: bindingsTable + }).then(() => { + DOM.toast(`Exchange ${e} bound to queue`) }) }) diff --git a/static/js/user.js b/static/js/user.js index 562afb9be0..13b69afb23 100644 --- a/static/js/user.js +++ b/static/js/user.js @@ -95,12 +95,11 @@ document.querySelector('#updateUser').addEventListener('submit', function (evt) } HTTP.submitForm(evt.target, 'PUT', url, { body, - toast: 'User updated', - callback: function () { - updateUser() - pwd.disabled = false - pwd.required = true - } + }).then(() =>{ + DOM.toast('User updated') + updateUser() + pwd.disabled = false + pwd.required = true }) }) diff --git a/static/js/users.js b/static/js/users.js index 35aa89a216..f9db59fc32 100644 --- a/static/js/users.js +++ b/static/js/users.js @@ -51,8 +51,9 @@ document.querySelector('#createUser').addEventListener('submit', function (evt) } HTTP.submitForm(evt.target, 'PUT', url, { body, - table: usersTable, - toast: toastText + table: usersTable + }).then(()=> { + DOM.toast(toastText) }) }) From 885a4fa53ffc104c889ea9ad696849e71b28ea03 Mon Sep 17 00:00:00 2001 From: ThomasSarlin Date: Fri, 13 Mar 2026 16:28:33 +0100 Subject: [PATCH 08/10] Throw error if formSubmit request is failing --- static/js/http.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/http.js b/static/js/http.js index 38c3f14187..a9b511928c 100644 --- a/static/js/http.js +++ b/static/js/http.js @@ -70,7 +70,7 @@ async function submitForm (form, method, url, options = {}) { const { body, table } = options return request(method, url, { body }) .then(res => { - if (res?.is_error) return + if (res?.is_error) throw new Error(`HTTP error: ${res.status}`) if (table) table.reload() form.reset() }) From 5cd8e04b8e2eacf430795bd753ac79b23421dcb6 Mon Sep 17 00:00:00 2001 From: ThomasSarlin Date: Fri, 13 Mar 2026 16:36:58 +0100 Subject: [PATCH 09/10] lintfix --- static/js/federation.js | 2 +- static/js/shovels.js | 2 +- static/js/user.js | 4 ++-- static/js/users.js | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/static/js/federation.js b/static/js/federation.js index 95fd0942fd..cffdf3655a 100644 --- a/static/js/federation.js +++ b/static/js/federation.js @@ -87,7 +87,7 @@ document.querySelector('#createUpstream').addEventListener('submit', function (e HTTP.submitForm(evt.target, 'PUT', url, { body, table: upstreamsTable - }).then(()=> { + }).then(() => { DOM.toast(`Upstream ${name} saved`) }) }) diff --git a/static/js/shovels.js b/static/js/shovels.js index a11572b091..37571f719c 100644 --- a/static/js/shovels.js +++ b/static/js/shovels.js @@ -213,7 +213,7 @@ document.querySelector('#createShovel').addEventListener('submit', function (evt HTTP.submitForm(evt.target, 'PUT', url, { body, table: dataSource - }).then(()=> { + }).then(() => { DOM.toast(`Shovel ${name} saved`) }) }) diff --git a/static/js/user.js b/static/js/user.js index 13b69afb23..72140d96c8 100644 --- a/static/js/user.js +++ b/static/js/user.js @@ -94,8 +94,8 @@ document.querySelector('#updateUser').addEventListener('submit', function (evt) body.password = data.get('password') } HTTP.submitForm(evt.target, 'PUT', url, { - body, - }).then(() =>{ + body + }).then(() => { DOM.toast('User updated') updateUser() pwd.disabled = false diff --git a/static/js/users.js b/static/js/users.js index f9db59fc32..3222a18212 100644 --- a/static/js/users.js +++ b/static/js/users.js @@ -1,6 +1,7 @@ import * as HTTP from './http.js' import * as Helpers from './helpers.js' import * as Table from './table.js' +import * as DOM from './dom.js' let usersTable = null HTTP.request('GET', 'api/permissions').then(permissions => { @@ -52,7 +53,7 @@ document.querySelector('#createUser').addEventListener('submit', function (evt) HTTP.submitForm(evt.target, 'PUT', url, { body, table: usersTable - }).then(()=> { + }).then(() => { DOM.toast(toastText) }) }) From 42dbf7fad48525630fd65708ba7a3e65cb73b0af Mon Sep 17 00:00:00 2001 From: ThomasSarlin Date: Fri, 13 Mar 2026 17:01:10 +0100 Subject: [PATCH 10/10] Throw res so that it can be parsed by both the alertHandler and the different catches in exchange and queue --- static/js/http.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/http.js b/static/js/http.js index a9b511928c..f657d4963f 100644 --- a/static/js/http.js +++ b/static/js/http.js @@ -70,7 +70,7 @@ async function submitForm (form, method, url, options = {}) { const { body, table } = options return request(method, url, { body }) .then(res => { - if (res?.is_error) throw new Error(`HTTP error: ${res.status}`) + if (res?.is_error) throw res if (table) table.reload() form.reset() })