feat(dashboard): add status filter pills and dim stopped services
- Add All/Running/Stopped filter pills with live counts in toolbar - Dim stopped service cards (55% opacity, red left border) - Cards brighten on hover for easy interaction Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+66
-1
@@ -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) {
|
||||
? `<span class="card-swarm-badge">Swarm</span>`
|
||||
: "";
|
||||
|
||||
const stoppedClass = !isRunning ? " stopped" : "";
|
||||
|
||||
return `
|
||||
<div class="service-card">
|
||||
<div class="service-card${stoppedClass}">
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<span class="card-name">${escHtml(s.name)}</span>
|
||||
@@ -685,6 +742,14 @@ function bindEvents() {
|
||||
});
|
||||
}
|
||||
|
||||
// Status filter pills
|
||||
$$(".filter-pill").forEach((pill) => {
|
||||
pill.addEventListener("click", () => {
|
||||
statusFilter = pill.dataset.filter;
|
||||
renderMain();
|
||||
});
|
||||
});
|
||||
|
||||
// Search
|
||||
const searchInput = $("#search-input");
|
||||
if (searchInput) {
|
||||
|
||||
Reference in New Issue
Block a user