From e1cfd1ba860aaa397532d6a532f4ade20262b9f7 Mon Sep 17 00:00:00 2001 From: Madhav Kandukuri Date: Mon, 10 Nov 2025 13:05:52 +0530 Subject: [PATCH 1/3] Fix select all in add server Signed-off-by: Madhav Kandukuri --- mcpgateway/static/admin.js | 147 ++++++++++++++++++++++++++++++++ mcpgateway/templates/admin.html | 62 ++------------ 2 files changed, 152 insertions(+), 57 deletions(-) diff --git a/mcpgateway/static/admin.js b/mcpgateway/static/admin.js index fefd09f6e..b80625232 100644 --- a/mcpgateway/static/admin.js +++ b/mcpgateway/static/admin.js @@ -5105,6 +5105,25 @@ async function editServer(serverId) { openModal("server-edit-modal"); + // Initialize the select handlers for resources and prompts in the edit modal + initResourceSelect( + 'edit-server-resources', + 'selectedEditResourcesPills', + 'selectedEditResourcesWarning', + 6, + 'selectAllEditResourcesBtn', + 'clearAllEditResourcesBtn' + ); + + initPromptSelect( + 'edit-server-prompts', + 'selectedEditPromptsPills', + 'selectedEditPromptsWarning', + 6, + 'selectAllEditPromptsBtn', + 'clearAllEditPromptsBtn' + ); + // Use multiple approaches to ensure checkboxes get set setEditServerAssociations(server); setTimeout(() => setEditServerAssociations(server), 100); @@ -5452,6 +5471,134 @@ if (window.htmx && !window._toolsHtmxHandlerAttached) { }); } +// Set up HTMX handler for auto-checking newly loaded resources when Select All is active +if (window.htmx && !window._resourcesHtmxHandlerAttached) { + window._resourcesHtmxHandlerAttached = true; + + window.htmx.on("htmx:afterSettle", function (evt) { + // Only handle resource pagination requests + if ( + evt.detail.pathInfo && + evt.detail.pathInfo.requestPath && + evt.detail.pathInfo.requestPath.includes("/admin/resources/partial") + ) { + setTimeout(() => { + // Find the container + let container = null; + const target = evt.detail.target; + + if (target && target.id === "edit-server-resources") { + container = target; + } else if (target && target.id === "associatedResources") { + container = target; + } else if (target) { + container = + target.closest("#associatedResources") || + target.closest("#edit-server-resources"); + } + + if (!container) { + const editModal = document.getElementById("server-edit-modal"); + const isEditModalOpen = editModal && !editModal.classList.contains("hidden"); + + if (isEditModalOpen) { + container = document.getElementById("edit-server-resources"); + } else { + container = document.getElementById("associatedResources"); + } + } + + if (container) { + const newCheckboxes = container.querySelectorAll( + "input[data-auto-check=true]", + ); + + const selectAllInput = container.querySelector( + 'input[name="selectAllResources"]', + ); + + // Check if Select All is active + if (selectAllInput && selectAllInput.value === "true") { + newCheckboxes.forEach((cb) => { + cb.checked = true; + cb.removeAttribute("data-auto-check"); + }); + + if (newCheckboxes.length > 0) { + const event = new Event("change", { bubbles: true }); + container.dispatchEvent(event); + } + } + } + }, 10); + } + }); +} + +// Set up HTMX handler for auto-checking newly loaded prompts when Select All is active +if (window.htmx && !window._promptsHtmxHandlerAttached) { + window._promptsHtmxHandlerAttached = true; + + window.htmx.on("htmx:afterSettle", function (evt) { + // Only handle prompt pagination requests + if ( + evt.detail.pathInfo && + evt.detail.pathInfo.requestPath && + evt.detail.pathInfo.requestPath.includes("/admin/prompts/partial") + ) { + setTimeout(() => { + // Find the container + let container = null; + const target = evt.detail.target; + + if (target && target.id === "edit-server-prompts") { + container = target; + } else if (target && target.id === "associatedPrompts") { + container = target; + } else if (target) { + container = + target.closest("#associatedPrompts") || + target.closest("#edit-server-prompts"); + } + + if (!container) { + const editModal = document.getElementById("server-edit-modal"); + const isEditModalOpen = editModal && !editModal.classList.contains("hidden"); + + if (isEditModalOpen) { + container = document.getElementById("edit-server-prompts"); + } else { + container = document.getElementById("associatedPrompts"); + } + } + + if (container) { + const newCheckboxes = container.querySelectorAll( + "input[data-auto-check=true]", + ); + + const selectAllInput = container.querySelector( + 'input[name="selectAllPrompts"]', + ); + + // Check if Select All is active + if (selectAllInput && selectAllInput.value === "true") { + newCheckboxes.forEach((cb) => { + cb.checked = true; + cb.removeAttribute("data-auto-check"); + }); + + if (newCheckboxes.length > 0) { + const event = new Event("change", { bubbles: true }); + container.dispatchEvent(event); + } + } + } + }, 10); + } + }); +} + // =================================================================== // ENHANCED TAB HANDLING with Better Error Management // =================================================================== diff --git a/mcpgateway/templates/admin.html b/mcpgateway/templates/admin.html index 3d1b8feb5..6a10f81f4 100644 --- a/mcpgateway/templates/admin.html +++ b/mcpgateway/templates/admin.html @@ -12639,63 +12639,11 @@

} }); - // Select All / Clear All handlers for create server form - document.addEventListener('DOMContentLoaded', function() { - // Resources Select All / Clear All for create form - const selectAllResourcesBtn = document.getElementById('selectAllResourcesBtn'); - const clearAllResourcesBtn = document.getElementById('clearAllResourcesBtn'); - if (selectAllResourcesBtn && clearAllResourcesBtn) { - selectAllResourcesBtn.addEventListener('click', function() { - const checkboxes = document.querySelectorAll('#associatedResources input[type="checkbox"][name="associatedResources"]'); - checkboxes.forEach(checkbox => checkbox.checked = true); - }); - clearAllResourcesBtn.addEventListener('click', function() { - const checkboxes = document.querySelectorAll('#associatedResources input[type="checkbox"][name="associatedResources"]'); - checkboxes.forEach(checkbox => checkbox.checked = false); - }); - } - - // Prompts Select All / Clear All for create form - const selectAllPromptsBtn = document.getElementById('selectAllPromptsBtn'); - const clearAllPromptsBtn = document.getElementById('clearAllPromptsBtn'); - if (selectAllPromptsBtn && clearAllPromptsBtn) { - selectAllPromptsBtn.addEventListener('click', function() { - const checkboxes = document.querySelectorAll('#associatedPrompts input[type="checkbox"][name="associatedPrompts"]'); - checkboxes.forEach(checkbox => checkbox.checked = true); - }); - clearAllPromptsBtn.addEventListener('click', function() { - const checkboxes = document.querySelectorAll('#associatedPrompts input[type="checkbox"][name="associatedPrompts"]'); - checkboxes.forEach(checkbox => checkbox.checked = false); - }); - } - - // Select All / Clear All handlers for edit server form (should already exist but let's add them) - const selectAllEditResourcesBtn = document.getElementById('selectAllEditResourcesBtn'); - const clearAllEditResourcesBtn = document.getElementById('clearAllEditResourcesBtn'); - if (selectAllEditResourcesBtn && clearAllEditResourcesBtn) { - selectAllEditResourcesBtn.addEventListener('click', function() { - const checkboxes = document.querySelectorAll('#edit-server-resources input[type="checkbox"][name="associatedResources"]'); - checkboxes.forEach(checkbox => checkbox.checked = true); - }); - clearAllEditResourcesBtn.addEventListener('click', function() { - const checkboxes = document.querySelectorAll('#edit-server-resources input[type="checkbox"][name="associatedResources"]'); - checkboxes.forEach(checkbox => checkbox.checked = false); - }); - } - - const selectAllEditPromptsBtn = document.getElementById('selectAllEditPromptsBtn'); - const clearAllEditPromptsBtn = document.getElementById('clearAllEditPromptsBtn'); - if (selectAllEditPromptsBtn && clearAllEditPromptsBtn) { - selectAllEditPromptsBtn.addEventListener('click', function() { - const checkboxes = document.querySelectorAll('#edit-server-prompts input[type="checkbox"][name="associatedPrompts"]'); - checkboxes.forEach(checkbox => checkbox.checked = true); - }); - clearAllEditPromptsBtn.addEventListener('click', function() { - const checkboxes = document.querySelectorAll('#edit-server-prompts input[type="checkbox"][name="associatedPrompts"]'); - checkboxes.forEach(checkbox => checkbox.checked = false); - }); - } - }); + // NOTE: Select All / Clear All handlers for Resources and Prompts are now handled + // by initResourceSelect() and initPromptSelect() functions in admin.js. + // Those functions properly fetch ALL IDs from the server and maintain state, + // so items that load via pagination are also included in the selection. + // The handlers are attached via HTMX's hx-on::after-swap attribute. // Download sample JSON for bulk import function downloadSampleJSON() { From 1d5f546ab4445a4f4b8c6784012f09e123dcf946 Mon Sep 17 00:00:00 2001 From: Madhav Kandukuri Date: Mon, 10 Nov 2025 13:11:28 +0530 Subject: [PATCH 2/3] Add pagination in edit server Signed-off-by: Madhav Kandukuri --- mcpgateway/static/admin.js | 62 +++++++++++++++++++++++++++++++++ mcpgateway/templates/admin.html | 50 +++++++++++++------------- 2 files changed, 86 insertions(+), 26 deletions(-) diff --git a/mcpgateway/static/admin.js b/mcpgateway/static/admin.js index b80625232..f898078f8 100644 --- a/mcpgateway/static/admin.js +++ b/mcpgateway/static/admin.js @@ -5103,6 +5103,24 @@ async function editServer(serverId) { ); } + // Set associated resources data attribute on the container + const editResourcesContainer = document.getElementById("edit-server-resources"); + if (editResourcesContainer && server.associatedResources) { + editResourcesContainer.setAttribute( + "data-server-resources", + JSON.stringify(server.associatedResources), + ); + } + + // Set associated prompts data attribute on the container + const editPromptsContainer = document.getElementById("edit-server-prompts"); + if (editPromptsContainer && server.associatedPrompts) { + editPromptsContainer.setAttribute( + "data-server-prompts", + JSON.stringify(server.associatedPrompts), + ); + } + openModal("server-edit-modal"); // Initialize the select handlers for resources and prompts in the edit modal @@ -5529,6 +5547,28 @@ if (window.htmx && !window._resourcesHtmxHandlerAttached) { container.dispatchEvent(event); } } + + // Also check for edit mode: pre-select items based on server's associated resources + const dataAttr = container.getAttribute("data-server-resources"); + if (dataAttr) { + try { + const associatedResourceIds = JSON.parse(dataAttr); + newCheckboxes.forEach((cb) => { + const checkboxValue = parseInt(cb.value); + if (associatedResourceIds.includes(checkboxValue)) { + cb.checked = true; + } + cb.removeAttribute("data-auto-check"); + }); + + if (newCheckboxes.length > 0) { + const event = new Event("change", { bubbles: true }); + container.dispatchEvent(event); + } + } catch (e) { + console.error("Error parsing data-server-resources:", e); + } + } } }, 10); } @@ -5593,6 +5633,28 @@ if (window.htmx && !window._promptsHtmxHandlerAttached) { container.dispatchEvent(event); } } + + // Also check for edit mode: pre-select items based on server's associated prompts + const dataAttr = container.getAttribute("data-server-prompts"); + if (dataAttr) { + try { + const associatedPromptIds = JSON.parse(dataAttr); + newCheckboxes.forEach((cb) => { + const checkboxValue = parseInt(cb.value); + if (associatedPromptIds.includes(checkboxValue)) { + cb.checked = true; + } + cb.removeAttribute("data-auto-check"); + }); + + if (newCheckboxes.length > 0) { + const event = new Event("change", { bubbles: true }); + container.dispatchEvent(event); + } + } catch (e) { + console.error("Error parsing data-server-prompts:", e); + } + } } }, 10); } diff --git a/mcpgateway/templates/admin.html b/mcpgateway/templates/admin.html index 6a10f81f4..1a702c800 100644 --- a/mcpgateway/templates/admin.html +++ b/mcpgateway/templates/admin.html @@ -8876,20 +8876,19 @@

- {% for resource in resources %} - - {% endfor %} + +
+ + + + + Loading resources... +