Skip to content
This repository was archived by the owner on Mar 15, 2026. It is now read-only.

Commit 4981938

Browse files
Show status on agents list page
1 parent a57ec32 commit 4981938

2 files changed

Lines changed: 95 additions & 26 deletions

File tree

src/components/agents/AgentDetailsPage.jsx

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
1-
import { useQueryClient } from "@tanstack/react-query";
21
import { useAgentQuery } from "api/agents.js";
32
import NativeButtonGroup from "components/form/NativeButtonGroup.jsx";
3+
import Breadcrumb from "components/ui/Breadcrumb.jsx";
44
import PrimaryButton from "components/ui/buttons/Primary.jsx";
55
import CommandTerminal from "components/ui/CommandTerminal.jsx";
6-
import { WebsocketContext } from "contexts/WebsocketContext.jsx";
7-
import { useContext, useState } from "react";
8-
import { useNavigate, useParams } from "react-router-dom";
6+
import Tag from "components/ui/Tag.jsx";
7+
import { useState } from "react";
8+
import { Link, useParams } from "react-router-dom";
99
import Loading from "../ui/Loading.jsx";
1010

11+
const pingedRecently = (lastPingAt) => {
12+
if (!lastPingAt) return false;
13+
const lastPingDate = new Date(lastPingAt);
14+
const now = new Date();
15+
const diffMinutes = (now - lastPingDate) / 1000 / 60;
16+
return diffMinutes < 5; // Consider online if pinged within the last 5 minutes
17+
}
18+
19+
1120
const AgentDetailsPage = () => {
1221
const { agentId } = useParams();
13-
const navigate = useNavigate();
14-
const queryClient = useQueryClient();
1522

1623
const { data: agent, isLoading } = useAgentQuery(agentId);
1724

1825
const [terminalVisibility, setTerminalVisibility] = useState(false);
19-
const wsContextData = useContext(WebsocketContext);
2026

2127
const connect = () => {
2228
setTerminalVisibility(true);
@@ -29,9 +35,34 @@ const AgentDetailsPage = () => {
2935

3036
return (
3137
<div>
32-
{agent.os}
38+
<Breadcrumb>
39+
<Link to="/agents">Agents</Link>
40+
<Link>{agent.name}</Link>
41+
</Breadcrumb>
42+
43+
44+
<Tag>{agent.os}</Tag> {pingedRecently(agent.lastPingAt) ? (<span className="tag is-success">Online</span>) : (
45+
<span className="tag is-warning">Offline</span>
46+
)}
47+
48+
<h3 className="title is-3">Agent: {agent.name}</h3>
49+
50+
<dl className="content">
51+
<dt>Hostname:</dt>
52+
<dd>{agent.hostname}</dd>
53+
54+
<dt>IP Address:</dt>
55+
<dd>{agent.ip}</dd>
56+
57+
<dt>Listen Address:</dt>
58+
<dd>{agent.listenAddr}</dd>
59+
60+
<dt>Last Booted At:</dt>
61+
<dd>{agent.lastBootAt}</dd>
3362

34-
<h3 className="title is-3">Agent terminal</h3>
63+
<dt>Last Pinged At:</dt>
64+
<dd>{agent.lastPingAt}</dd>
65+
</dl>
3566

3667
<NativeButtonGroup>
3768
<PrimaryButton onClick={connect} disabled={terminalVisibility === true}>

src/components/agents/AgentsListPage.jsx

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
import { useAgentsQuery } from "api/agents.js";
22
import { requestAgentDelete, requestAgentPost } from "api/requests/agents.js";
3+
import HorizontalLabelledField from "components/form/HorizontalLabelledField.jsx";
4+
import NativeButton from "components/form/NativeButton.jsx";
5+
import NativeInput from "components/form/NativeInput.jsx";
36
import NativeTable from "components/ui/tables/NativeTable.jsx";
47
import { useState } from "react";
58
import { Link } from "react-router-dom";
69

10+
const pingedRecently = (lastPingAt) => {
11+
if (!lastPingAt) return false;
12+
const lastPingDate = new Date(lastPingAt);
13+
const now = new Date();
14+
const diffMinutes = (now - lastPingDate) / 1000 / 60;
15+
return diffMinutes < 5; // Consider online if pinged within the last 5 minutes
16+
}
17+
718
const AgentsListPage = () => {
819
const { data: agents, isLoading, refetch } = useAgentsQuery();
920

@@ -16,30 +27,51 @@ const AgentsListPage = () => {
1627
const formData = new FormData(event.target);
1728
const data = Object.fromEntries(formData.entries());
1829
requestAgentPost(data).then((response) => {
19-
if (response.ok) {
20-
event.target.reset();
21-
setAgentCredentials(response.data);
22-
alert("Agent added successfully");
23-
refetch();
30+
if (response.status === 201) {
31+
return response.json();
2432
} else {
2533
alert("Failed to add agent");
2634
}
35+
}).then((data) => {
36+
if (data) {
37+
event.target.reset();
38+
setAgentCredentials(data);
39+
refetch();
40+
alert("Agent added successfully");
41+
}
2742
});
2843
}
2944

3045
const columns = [
46+
{
47+
header: "Status",
48+
cell: (agent) => <>
49+
{pingedRecently(agent.lastPingAt) ? (<span className="tag is-success">Online</span>) : (
50+
<span className="tag is-warning">Offline</span>
51+
)}
52+
</>
53+
},
3154
{
3255
header: "Enabled",
3356
cell: (agent) => (agent.active ? <>True</> : <>False</>),
57+
enabled: false
3458
},
3559
{
36-
header: "Name",
60+
header: "Client ID",
3761
cell: (agent) => (
3862
<>
3963
<Link to={`/agents/${agent.id}`}>{agent.clientId}</Link>
4064
</>
4165
),
4266
},
67+
{
68+
header: "Name",
69+
cell: (agent) => (
70+
<>
71+
<Link to={`/agents/${agent.id}`}>{agent.name}</Link>
72+
</>
73+
),
74+
},
4375
{
4476
header: "Hostname",
4577
property: "hostname",
@@ -84,8 +116,8 @@ const AgentsListPage = () => {
84116
if (window.confirm("Are you sure you want to delete this agent?")) {
85117
requestAgentDelete(agent.id).then((response) => {
86118
if (response.ok) {
87-
alert("Agent deleted successfully");
88119
refetch();
120+
alert("Agent deleted successfully");
89121
} else {
90122
alert("Failed to delete agent");
91123
}
@@ -98,23 +130,29 @@ const AgentsListPage = () => {
98130
];
99131

100132
return <>
101-
<NativeTable columns={columns} rows={agents} rowId={(agent) => agent.id}></NativeTable>;
133+
<NativeTable columns={columns} rows={agents} rowId={(agent) => agent.id}></NativeTable>
102134

103135
{agentCredentials && (
104136
<div>
105-
The agent credentials are:
137+
The agent credentials are (please copy them as they are only displayed once):
106138
<pre>{JSON.stringify(agentCredentials, null, 2)}</pre>
107-
Please copy them, they are only displayed once.
139+
108140
</div>
109141
)}
110142

111-
<form method="POST" action="/api/agents" onSubmit={onSubmit}>
112-
<input type="text" name="clientId" placeholder="Client ID" required />
113-
<input type="text" name="hostname" placeholder="Hostname" required />
114-
<input type="text" name="listenAddr" placeholder="Listen Address" required />
115-
<input type="submit" value="Add new agent" />
116-
</form>
143+
<details>
144+
<summary>Add agent</summary>
145+
<form method="POST" action="/api/agents" onSubmit={onSubmit}>
146+
<HorizontalLabelledField label="Name" control={<NativeInput type="text" name="name" placeholder="Name" required />} />
147+
148+
<HorizontalLabelledField label="Hostname" control={<NativeInput type="text" name="hostname" placeholder="Hostname" required />} />
149+
150+
<HorizontalLabelledField label="Listen Address" control={<NativeInput type="text" name="listenAddr" placeholder="Listen Address" required />} />
151+
152+
<NativeButton type="submit" className="button is-primary">Add new agent</NativeButton>
153+
</form>
154+
</details>
117155
</>
118-
};
156+
}
119157

120158
export default AgentsListPage;

0 commit comments

Comments
 (0)