diff --git a/packages/web_ui/src/components/InstanceList.jsx b/packages/web_ui/src/components/InstanceList.jsx
new file mode 100644
index 00000000..4a010ffa
--- /dev/null
+++ b/packages/web_ui/src/components/InstanceList.jsx
@@ -0,0 +1,58 @@
+import React from "react";
+import { useHistory } from "react-router-dom";
+import { Table } from "antd";
+
+import { useSlaveList } from "../model/slave";
+
+const strcmp = new Intl.Collator(undefined, { numerice: "true", sensitivity: "base" }).compare;
+
+
+export default function InstanceList(props) {
+ let history = useHistory();
+ let [slaveList] = useSlaveList();
+
+ function slaveName(slaveId) {
+ let slave = slaveList.find(s => s.id === slaveId);
+ if (slave) {
+ return slave.name;
+ }
+ return String(slaveId);
+ }
+
+ let columns = [
+ {
+ title: "Name",
+ dataIndex: "name",
+ defaultSortOrder: "ascend",
+ sorter: (a, b) => strcmp(a["name"], b["name"]),
+ },
+ {
+ title: "Assigned Slave",
+ key: "assigned_slave",
+ render: instance => slaveName(instance["assigned_slave"]),
+ sorter: (a, b) => strcmp(slaveName(a["assigned_slave"]), slaveName(b["assigned_slave"])),
+ },
+ {
+ title: "Status",
+ dataIndex: "status",
+ sorter: (a, b) => strcmp(a["status"], b["status"]),
+ },
+ ];
+
+ if (props.hideAssignedSlave) {
+ columns.splice(1, 1);
+ }
+
+ return
instance["id"]}
+ pagination={false}
+ onRow={(record, rowIndex) => ({
+ onClick: event => {
+ history.push(`/instances/${record.id}/view`);
+ },
+ })}
+ />;
+}
diff --git a/packages/web_ui/src/components/InstancesPage.jsx b/packages/web_ui/src/components/InstancesPage.jsx
index 85996fe2..fb6dbd71 100644
--- a/packages/web_ui/src/components/InstancesPage.jsx
+++ b/packages/web_ui/src/components/InstancesPage.jsx
@@ -1,6 +1,6 @@
import React, { useContext, useState } from "react";
import { useHistory } from "react-router-dom";
-import { Button, Form, Input, Modal, PageHeader, Table } from "antd";
+import { Button, Form, Input, Modal, PageHeader } from "antd";
import { libConfig, libLink } from "@clusterio/lib";
@@ -8,10 +8,8 @@ import { useAccount } from "../model/account";
import ControlContext from "./ControlContext";
import PageLayout from "./PageLayout";
import { useInstanceList } from "../model/instance";
+import InstanceList from "./InstanceList";
import { notifyErrorHandler } from "../util/notify";
-import { useSlaveList } from "../model/slave";
-
-const strcmp = new Intl.Collator(undefined, { numerice: "true", sensitivity: "base" }).compare;
function CreateInstanceButton(props) {
@@ -61,19 +59,9 @@ function CreateInstanceButton(props) {
}
export default function InstancesPage() {
- let history = useHistory();
let account = useAccount();
- let [slaveList] = useSlaveList();
let [instanceList] = useInstanceList();
- function slaveName(slaveId) {
- let slave = slaveList.find(s => s.id === slaveId);
- if (slave) {
- return slave.name;
- }
- return String(slaveId);
- }
-
return
}
/>
- strcmp(a["name"], b["name"]),
- },
- {
- title: "Assigned Slave",
- key: "assigned_slave",
- render: instance => slaveName(instance["assigned_slave"]),
- sorter: (a, b) => strcmp(slaveName(a["assigned_slave"]), slaveName(b["assigned_slave"])),
- },
- {
- title: "Status",
- dataIndex: "status",
- sorter: (a, b) => strcmp(a["status"], b["status"]),
- },
- ]}
- dataSource={instanceList}
- rowKey={instance => instance["id"]}
- pagination={false}
- onRow={(record, rowIndex) => ({
- onClick: event => {
- history.push(`/instances/${record.id}/view`);
- },
- })}
- />
+
;
}
diff --git a/packages/web_ui/src/components/SlaveViewPage.jsx b/packages/web_ui/src/components/SlaveViewPage.jsx
new file mode 100644
index 00000000..f4076073
--- /dev/null
+++ b/packages/web_ui/src/components/SlaveViewPage.jsx
@@ -0,0 +1,56 @@
+import React, { useContext, useState } from "react";
+import { Descriptions, PageHeader, Spin, Typography } from "antd";
+import { useParams } from "react-router-dom";
+
+import InstanceList from "./InstanceList";
+import { useAccount } from "../model/account";
+import { useInstanceList } from "../model/instance";
+import { useSlave } from "../model/slave";
+import PageLayout from "./PageLayout";
+
+const { Title } = Typography;
+
+
+export default function SlaveViewPage(props) {
+ let params = useParams();
+ let slaveId = Number(params.id);
+ let account = useAccount();
+ let [instanceList] = useInstanceList();
+ let [slave] = useSlave(slaveId);
+
+ instanceList = instanceList.filter(instance => instance["assigned_slave"] === slaveId);
+
+ let nav = [{ name: "Slaves", path: "/slaves" }, { name: slave.name || slaveId }];
+ if (slave.loading) {
+ return ;
+ }
+
+ if (slave.missing) {
+ return
+
+ Slave with id {slaveId} was not found on the master server.
+ ;
+ }
+
+ return
+
+
+
+ {slave["name"]}
+ {slave["connected"] ? "yes" : "no"}
+ {slave["agent"]}
+ {slave["version"]}
+
+
+ {account.hasPermission("core.instance.list") && <>
+ Instances
+
+ >}
+ ;
+}
diff --git a/packages/web_ui/src/components/SlavesPage.jsx b/packages/web_ui/src/components/SlavesPage.jsx
index f94001a8..9333752a 100644
--- a/packages/web_ui/src/components/SlavesPage.jsx
+++ b/packages/web_ui/src/components/SlavesPage.jsx
@@ -1,4 +1,5 @@
import React, { useContext, useRef, useState } from "react";
+import { useHistory } from "react-router-dom";
import { Button, Form, Input, Modal, PageHeader, Table } from "antd";
import { libLink } from "@clusterio/lib";
@@ -75,6 +76,7 @@ function GenerateSlaveTokenButton(props) {
export default function SlavesPage() {
let account = useAccount();
+ let history = useHistory();
let [slaveList] = useSlaveList();
return
@@ -112,6 +114,11 @@ export default function SlavesPage() {
dataSource={slaveList}
rowKey={slave => slave["id"]}
pagination={false}
+ onRow={(record, rowIndex) => ({
+ onClick: event => {
+ history.push(`/slaves/${record.id}/view`);
+ },
+ })}
/>
;
};
diff --git a/packages/web_ui/src/index.jsx b/packages/web_ui/src/index.jsx
index a692e723..febfde98 100644
--- a/packages/web_ui/src/index.jsx
+++ b/packages/web_ui/src/index.jsx
@@ -15,6 +15,7 @@ export { default as MasterConfigTree } from "./components/MasterConfigTree";
export { default as AssignInstanceModal } from "./components/AssignInstanceModal";
export { default as InstanceConsole } from "./components/InstanceConsole";
+export { default as InstanceList } from "./components/InstanceList";
export { default as InstanceRcon } from "./components/InstanceRcon";
export { default as PageLayout } from "./components/PageLayout";
diff --git a/packages/web_ui/src/pages.jsx b/packages/web_ui/src/pages.jsx
index b643b0e5..cec0de30 100644
--- a/packages/web_ui/src/pages.jsx
+++ b/packages/web_ui/src/pages.jsx
@@ -2,6 +2,7 @@ import React from "react";
import MasterPage from "./components/MasterPage";
import SlavesPage from "./components/SlavesPage";
+import SlaveViewPage from "./components/SlaveViewPage";
import InstancesPage from "./components/InstancesPage";
import InstanceViewPage from "./components/InstanceViewPage";
import UsersPage from "./components/UsersPage";
@@ -24,6 +25,11 @@ export const pages = [
permission: "core.slave.list",
content: ,
},
+ {
+ path: "/slaves/:id/view",
+ sidebarPath: "/slaves",
+ content: ,
+ },
{
path: "/instances",
sidebarName: "Instances",