diff --git a/server/static/app.js b/server/static/app.js index 26f8116..8257ed8 100644 --- a/server/static/app.js +++ b/server/static/app.js @@ -12,6 +12,7 @@ let logPanelOpen = false; let modalOpen = false; let editingGroupId = null; // null = creating, string = editing let searchQuery = ""; +let statusFilter = "all"; // "all", "running", "stopped" // --------------------------------------------------------------------------- // DOM references @@ -199,6 +200,39 @@ function renderMain() { renderServices(filtered); } +function getPreStatusFilteredServices() { + let list = [...services]; + + if (currentView === "all") { + // show all + } else if (currentView.startsWith("node:")) { + const nodeName = currentView.slice(5); + list = list.filter((s) => s.node === nodeName); + } else { + const group = groups.find((g) => g.id === currentView); + if (group) { + const svcSet = new Set( + group.services.map((s) => s.node + ":" + s.container) + ); + list = list.filter((s) => svcSet.has(s.node + ":" + s.name)); + } else { + list = []; + } + } + + if (searchQuery) { + const q = searchQuery.toLowerCase(); + list = list.filter( + (s) => + s.name.toLowerCase().includes(q) || + s.image.toLowerCase().includes(q) || + s.node.toLowerCase().includes(q) + ); + } + + return list; +} + function getFilteredServices() { let list = [...services]; @@ -232,6 +266,13 @@ function getFilteredServices() { ); } + // Status filter + if (statusFilter === "running") { + list = list.filter((s) => s.status === "running"); + } else if (statusFilter === "stopped") { + list = list.filter((s) => s.status !== "running"); + } + return list; } @@ -251,6 +292,20 @@ function renderToolbar(filtered) { const countEl = $("#service-count"); countEl.textContent = filtered.length + " service" + (filtered.length !== 1 ? "s" : ""); + // Status filter counts (from pre-status-filtered list) + const preFiltered = getPreStatusFilteredServices(); + const runningCount = preFiltered.filter((s) => s.status === "running").length; + const stoppedCount = preFiltered.filter((s) => s.status !== "running").length; + const countRunEl = $("#count-running"); + const countStopEl = $("#count-stopped"); + if (countRunEl) countRunEl.textContent = runningCount; + if (countStopEl) countStopEl.textContent = stoppedCount; + + // Update active pill + $$(".filter-pill").forEach((pill) => { + pill.classList.toggle("active", pill.dataset.filter === statusFilter); + }); + // Group actions visibility const groupActions = $("#group-actions"); const isGroupView = @@ -311,8 +366,10 @@ function renderServiceCard(s) { ? `Swarm` : ""; + const stoppedClass = !isRunning ? " stopped" : ""; + return ` -