Skip to content

Commit 80bdbed

Browse files
committed
delete fix
Signed-off-by: Rakhi Dutta <rakhibiswas@yahoo.com>
1 parent 9c98eba commit 80bdbed

File tree

3 files changed

+101
-6
lines changed

3 files changed

+101
-6
lines changed

mcpgateway/admin_ui/constants.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ export const DEFAULT_TEAMS_PER_PAGE = 10;
3434

3535
/**
3636
* Clear search functionality for different entity types
37+
*
38+
* IMPORTANT: All entity types used with handleFormSubmitAndRefresh (formHandlers.js)
39+
* must be registered here with their correct partialPath and targetSelector.
40+
* Failure to register new entity types will cause UI refresh issues after
41+
* delete/toggle operations (the entity will remain visible until manual page refresh).
42+
*
43+
* Each entity type must define:
44+
* - partialPath: The backend route for fetching the partial HTML (e.g., "a2a/partial")
45+
* - targetSelector: The DOM selector for the target element (e.g., "#agents-table")
3746
*/
3847
export const PANEL_SEARCH_CONFIG = {
3948
catalog: {

mcpgateway/admin_ui/formHandlers.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import { navigateAdmin } from "./navigation.js";
33
import { getCookie, isInactiveChecked } from "./utils.js";
44

55
// ===================================================================
6-
// INACTIVE ITEMS HANDLING
6+
// FORM SUBMISSION AND REFRESH HANDLING
77
// ===================================================================
8-
export const handleToggleSubmit = async function (event, type) {
8+
// Handles form submission (toggle/delete operations) and refreshes the table
9+
// via HTMX. Used by both handleSubmitWithConfirmation and handleDeleteSubmit.
10+
export const handleFormSubmitAndRefresh = async function (event, type) {
911
event.preventDefault();
1012

1113
const isInactiveCheckedBool = isInactiveChecked(type);
@@ -47,8 +49,11 @@ export const handleToggleSubmit = async function (event, type) {
4749
params.set("team_id", teamId);
4850
}
4951

50-
// Trigger HTMX request to refresh the table
52+
// Trigger HTMX request to refresh the table using PANEL_SEARCH_CONFIG
5153
const panelConfig = PANEL_SEARCH_CONFIG[type];
54+
if (!panelConfig) {
55+
console.warn(`No PANEL_SEARCH_CONFIG found for type: ${type}, using fallback pattern. Consider adding this type to PANEL_SEARCH_CONFIG in constants.js`);
56+
}
5257
const partialPath = panelConfig?.partialPath || `${type}/partial`;
5358
const targetSelector = panelConfig?.targetSelector || `#${type}-table`;
5459
const partialUrl = `${window.ROOT_PATH}/admin/${partialPath}?${params.toString()}`;
@@ -64,7 +69,7 @@ export const handleToggleSubmit = async function (event, type) {
6469
}
6570
} catch (e) {
6671
// Network error — still navigate so the user sees refreshed state.
67-
console.error("Toggle submit error:", e);
72+
console.error("Form submit error:", e);
6873
const fragment = TOGGLE_FRAGMENT_MAP[type] || type;
6974
const params = new URLSearchParams();
7075
if (teamId) {
@@ -74,6 +79,9 @@ export const handleToggleSubmit = async function (event, type) {
7479
}
7580
};
7681

82+
// Legacy alias for backward compatibility
83+
export const handleToggleSubmit = handleFormSubmitAndRefresh;
84+
7785
export const handleSubmitWithConfirmation = function (event, type) {
7886
event.preventDefault();
7987

@@ -83,7 +91,7 @@ export const handleSubmitWithConfirmation = function (event, type) {
8391
return false;
8492
}
8593

86-
return handleToggleSubmit(event, type);
94+
return handleFormSubmitAndRefresh(event, type);
8795
};
8896

8997
export const handleDeleteSubmit = function (
@@ -114,5 +122,5 @@ export const handleDeleteSubmit = function (
114122
}
115123

116124
const toggleType = inactiveType || type;
117-
return handleToggleSubmit(event, toggleType);
125+
return handleFormSubmitAndRefresh(event, toggleType);
118126
};

tests/unit/js/formHandlers.test.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,4 +278,82 @@ describe("handleDeleteSubmit", () => {
278278
})
279279
);
280280
});
281+
282+
test("uses PANEL_SEARCH_CONFIG for catalog/servers refresh", async () => {
283+
const form = document.createElement("form");
284+
form.id = "test-form";
285+
form.action = "/test";
286+
document.body.appendChild(form);
287+
288+
const tableDiv = document.createElement("div");
289+
tableDiv.id = "servers-table"; // Correct ID per PANEL_SEARCH_CONFIG for catalog
290+
document.body.appendChild(tableDiv);
291+
292+
const fetchMock = vi.fn().mockResolvedValue({ ok: true });
293+
global.fetch = fetchMock;
294+
295+
// Mock HTMX
296+
const htmxAjaxMock = vi.fn();
297+
global.window.htmx = { ajax: htmxAjaxMock };
298+
global.window.ROOT_PATH = "";
299+
300+
const event = { preventDefault: vi.fn(), target: form };
301+
302+
vi.spyOn(window, "confirm")
303+
.mockReturnValueOnce(true)
304+
.mockReturnValueOnce(false);
305+
306+
await handleDeleteSubmit(event, "server", "test-server", "catalog");
307+
308+
// Verify HTMX was called with correct partial path (servers/partial) and target selector (#servers-table)
309+
expect(htmxAjaxMock).toHaveBeenCalledWith(
310+
'GET',
311+
expect.stringContaining('/admin/servers/partial'),
312+
expect.objectContaining({
313+
target: '#servers-table', // targetSelector from PANEL_SEARCH_CONFIG for catalog
314+
swap: 'outerHTML'
315+
})
316+
);
317+
});
318+
319+
test("warns when PANEL_SEARCH_CONFIG is missing for a type", async () => {
320+
const form = document.createElement("form");
321+
form.id = "test-form";
322+
form.action = "/test";
323+
document.body.appendChild(form);
324+
325+
const fetchMock = vi.fn().mockResolvedValue({ ok: true });
326+
global.fetch = fetchMock;
327+
328+
// Mock HTMX
329+
const htmxAjaxMock = vi.fn();
330+
global.window.htmx = { ajax: htmxAjaxMock };
331+
global.window.ROOT_PATH = "";
332+
333+
// Mock console.warn
334+
const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
335+
336+
const event = { preventDefault: vi.fn(), target: form };
337+
338+
vi.spyOn(window, "confirm")
339+
.mockReturnValueOnce(true)
340+
.mockReturnValueOnce(false);
341+
342+
await handleDeleteSubmit(event, "unknown", "test-unknown", "unknown-type");
343+
344+
// Verify console.warn was called with appropriate message
345+
expect(consoleWarnSpy).toHaveBeenCalledWith(
346+
expect.stringContaining('No PANEL_SEARCH_CONFIG found for type: unknown-type')
347+
);
348+
349+
// Should still use fallback pattern
350+
expect(htmxAjaxMock).toHaveBeenCalledWith(
351+
'GET',
352+
expect.stringContaining('/admin/unknown-type/partial'),
353+
expect.objectContaining({
354+
target: '#unknown-type-table',
355+
swap: 'outerHTML'
356+
})
357+
);
358+
});
281359
});

0 commit comments

Comments
 (0)