diff --git a/src/components/agents/AgentChartStatus.vue b/src/components/agents/AgentChartStatus.vue index 4f2678b..e4b643f 100644 --- a/src/components/agents/AgentChartStatus.vue +++ b/src/components/agents/AgentChartStatus.vue @@ -10,22 +10,39 @@ const $api = inject("$api"); const agentStore = useAgentStore(); const { agents } = storeToRefs(agentStore); + const agentChartStatus = ref(null); const chart = ref(null); +const poll = ref(null); -onMounted(() => { - initChart(); +onMounted(async () => { window.addEventListener("resize", resizeChart); + + const [cfg, ag] = await Promise.allSettled([ + agentStore.getAgentConfig($api), + agentStore.getAgents($api), + ]); + if (cfg.status === "rejected") console.warn("[cmp] getAgentConfig failed:", cfg.reason); + if (ag.status === "rejected") console.error("[cmp] getAgents failed:", ag.reason); + + await initChart(); + + poll.value = setInterval(() => { + agentStore.getAgents($api).catch(err => console.warn("[poll] getAgents failed:", err)); + }, 10_000); }); onBeforeUnmount(() => { window.removeEventListener("resize", resizeChart); + if (poll.value) clearInterval(poll.value); + if (chart.value) { + chart.value.dispose(); // avoid ECharts memory leaks + chart.value = null; + } }); - watch(agents, () => { - setChartOption(); + if (chart.value) setChartOption(); }); - async function initChart() { chart.value = echarts.init(agentChartStatus.value); chart.value.showLoading("default", { @@ -34,27 +51,25 @@ async function initChart() { }); resizeChart(); - await agentStore.getAgents($api); - setChartOption(); - chart.value.hideLoading(); + try { + await agentStore.getAgents($api); // initial fetch + } finally { + setChartOption(); + chart.value.hideLoading(); + } } function setChartOption() { - chart.value.setOption({ + const option = { title: { - text: `${agents.value.length} Agent${agents.value.length == 1 ? "" : "s"}`, - textStyle: { - color: "white", - }, - }, - tooltip: { - trigger: "item", - }, - legend: { - show: false, + text: `${agents.value.length} Agent${agents.value.length === 1 ? "" : "s"}`, + textStyle: { color: "white" }, }, + tooltip: { trigger: "item" }, + legend: { show: false }, series: [ { + id: "agent-status", name: "Agent Status", type: "pie", radius: ["40%", "70%"], @@ -64,47 +79,39 @@ function setChartOption() { borderColor: "hsl(0deg, 0%, 14%)", borderWidth: 2, }, - label: { - show: false, - position: "center", - }, - labelLine: { - show: false, - }, + label: { show: false, position: "center" }, + labelLine: { show: false }, data: getChartData(), }, ], - }); + }; + chart.value.setOption(option, { notMerge: true, replaceMerge: ["series"], lazyUpdate: true }); } function getChartData() { if (!agents.value.length) return []; + const nowMs = agentStore.serverNowMs ?? Date.now(); + const cfg = agentStore.agentConfig; + return [ { name: "Alive (trusted)", - value: agents.value.filter( - (agent) => getAgentStatus(agent) === "alive" && agent.trusted - ).length, + value: agents.value.filter(a => getAgentStatus(a, nowMs, cfg) === "alive" && a.trusted).length, itemStyle: { color: "#4a9" }, }, { name: "Alive (untrusted)", - value: agents.value.filter( - (agent) => getAgentStatus(agent) === "alive" && !agent.trusted - ).length, + value: agents.value.filter(a => getAgentStatus(a, nowMs, cfg) === "alive" && !a.trusted).length, itemStyle: { color: "#F7DB89" }, }, { name: "Pending kill", - value: agents.value.filter( - (agent) => getAgentStatus(agent) === "pending kill" - ).length, + value: agents.value.filter(a => getAgentStatus(a, nowMs, cfg) === "pending kill").length, itemStyle: { color: "hsl(207deg, 61%, 53%)" }, }, { name: "Dead", - value: agents.value.filter((agent) => getAgentStatus(agent) === "dead") - .length, + value: agents.value.filter(a => getAgentStatus(a, nowMs, cfg) === "dead").length, itemStyle: { color: "#c31" }, }, ]; @@ -121,8 +128,5 @@ function resizeChart() { diff --git a/src/components/agents/DetailsModal.vue b/src/components/agents/DetailsModal.vue index 1797d66..3bfc091 100644 --- a/src/components/agents/DetailsModal.vue +++ b/src/components/agents/DetailsModal.vue @@ -1,178 +1,202 @@ +