diff --git a/package.json b/package.json
index 91699b17..cb959269 100644
--- a/package.json
+++ b/package.json
@@ -52,6 +52,7 @@
"humanize-duration": "^3.21.0",
"jwt-decode": "^2.2.0",
"luxon": "^1.21.3",
+ "moment": "^2.29.1",
"node-sass": "^4.12.0",
"novnc-core": "^0.2.1",
"object-to-formdata": "^3.0.6",
@@ -60,6 +61,7 @@
"react-bootstrap": "^1.0.0-beta.16",
"react-burger-menu": "^2.5.4",
"react-copy-to-clipboard": "^5.0.3",
+ "react-datetime": "^3.1.1",
"react-dev-utils": "^10.1.0",
"react-dom": "^16.9.0",
"react-pdf": "^4.1.0",
diff --git a/src/components/AdminPanelLayout/AdminPanelLayout.tsx b/src/components/AdminPanelLayout/AdminPanelLayout.tsx
index 8e744e6a..7491e8cf 100644
--- a/src/components/AdminPanelLayout/AdminPanelLayout.tsx
+++ b/src/components/AdminPanelLayout/AdminPanelLayout.tsx
@@ -20,10 +20,10 @@ interface AdminPanelLayoutProps {
}
const panes = [
- {label: 'Application Statistics', eventKey: '#statistics', icon: faChartBar, component: },
- {label: 'Cluster Management', eventKey: '#cluster-management', icon: faNetworkWired, component: },
- {label: 'User Management', eventKey: '#user-management', icon: faUsers, component: },
- {label: 'Downtime Scheduler', eventKey: '#downtime-scheduler', icon: faCalendarAlt, component: }
+ {label: 'Application Statistics', eventKey: '#statistics', component: },
+ {label: 'Cluster Management', eventKey: '#cluster-management', component: },
+ {label: 'User Management', eventKey: '#user-management', component: },
+ {label: 'Downtime Scheduler', eventKey: '#downtime-scheduler', component: }
] as const;
type AdminTabKeys = typeof panes[number]['eventKey'];
diff --git a/src/components/AdminPanelLayout/ClusterPane.module.scss b/src/components/AdminPanelLayout/ClusterPane.module.scss
new file mode 100644
index 00000000..afc183ee
--- /dev/null
+++ b/src/components/AdminPanelLayout/ClusterPane.module.scss
@@ -0,0 +1,11 @@
+@import "../../theme";
+
+.cluster-pane {
+ Button {
+ float: right;
+ margin-left: 15px;
+ }
+ p {
+ padding-top: 65px;
+ }
+}
diff --git a/src/components/AdminPanelLayout/ClusterPane.tsx b/src/components/AdminPanelLayout/ClusterPane.tsx
index 6722edbe..bb3a465c 100644
--- a/src/components/AdminPanelLayout/ClusterPane.tsx
+++ b/src/components/AdminPanelLayout/ClusterPane.tsx
@@ -1,5 +1,63 @@
-import React from 'react';
+import React, {useState} from 'react';
+import {Button, Col, Row} from 'react-bootstrap';
+import {Layout} from '../../pages/Layout/Layout';
+import styles from './ClusterPane.module.scss';
+import Node from './Node';
+import MaintenancePopup from './MaintenancePopup';
+import Datetime from 'react-datetime';
+// tslint:disable-next-line:no-import-side-effect
+import 'react-datetime/css/react-datetime.css';
-export const ClusterPane = () => (
- <>The cluster management panel is still under construction>
-);
+export const ClusterPane = (props: {clusterName: string}) => {
+
+ const [isOpen, setIsOpen] = useState(false);
+
+ const togglePopup = () => {
+ setIsOpen(!isOpen);
+ };
+
+ return (
+
+
+ {isOpen &&
+ Maintenance Scheduler
+ {props.clusterName}
+
+ >
+ }
+ handleClose={() => togglePopup()}
+ />
+ }
+
+
+ {props.clusterName}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/AdminPanelLayout/MaintenancePopup.module.scss b/src/components/AdminPanelLayout/MaintenancePopup.module.scss
new file mode 100644
index 00000000..026cd6f1
--- /dev/null
+++ b/src/components/AdminPanelLayout/MaintenancePopup.module.scss
@@ -0,0 +1,42 @@
+@import "../../theme";
+
+.popup-box {
+ position: fixed;
+ background: #00000050;
+ width: 100%;
+ height: 100vh;
+ top: 0;
+ left: 0;
+ z-index: 2;
+}
+
+.box {
+ position: relative;
+ width: 50%;
+ margin: 0 auto;
+ height: 550px;
+ //max-height: 150vh;
+ margin-top: calc(100vh - 85vh - 20px);
+ background: #fff;
+ border-radius: 4px;
+ padding: 20px;
+ border: 1px solid #999;
+ overflow: auto;
+ margin-right: 175px;
+}
+
+.close-icon {
+ content: 'x';
+ cursor: pointer;
+ position: fixed;
+ right: 165px;
+ top: 110px;
+ background: #ededed;
+ width: 25px;
+ height: 25px;
+ border-radius: 50%;
+ line-height: 20px;
+ text-align: center;
+ border: 1px solid #999;
+ font-size: 20px;
+}
diff --git a/src/components/AdminPanelLayout/MaintenancePopup.tsx b/src/components/AdminPanelLayout/MaintenancePopup.tsx
new file mode 100644
index 00000000..448c5234
--- /dev/null
+++ b/src/components/AdminPanelLayout/MaintenancePopup.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import styles from './MaintenancePopup.module.scss';
+
+const MaintenancePopup = (props: { handleClose: () => void; content: React.ReactNode }) => {
+ return (
+
+
+ x
+ {props.content}
+
+
+ );
+};
+
+export default MaintenancePopup;
diff --git a/src/components/AdminPanelLayout/Node.module.scss b/src/components/AdminPanelLayout/Node.module.scss
new file mode 100644
index 00000000..d8fd2d25
--- /dev/null
+++ b/src/components/AdminPanelLayout/Node.module.scss
@@ -0,0 +1,29 @@
+@import "../../theme";
+
+.node {
+ Button {
+ float: right;
+ margin: 5px;
+ }
+ span {
+ margin-left: 10px;
+ height: 30px;
+ width: 30px;
+ background-color: #bbb;
+ border-radius: 50%;
+ display: inline-block;
+ }
+ #good {
+ background-color: #009E0F;
+ }
+ #issues {
+ background-color: #FF9900;
+ }
+ #maintenance {
+ background-color: black;
+ }
+ #offline {
+ background-color: #CF2A27;
+ }
+ z-index: 1;
+}
diff --git a/src/components/AdminPanelLayout/Node.tsx b/src/components/AdminPanelLayout/Node.tsx
new file mode 100644
index 00000000..554d5ac3
--- /dev/null
+++ b/src/components/AdminPanelLayout/Node.tsx
@@ -0,0 +1,84 @@
+import React from 'react';
+import styles from './Node.module.scss';
+import {Button, Card} from 'react-bootstrap';
+
+// 0 is green, 1 is yellow, 2 is black, 4 is red
+enum NodeStatus {
+ Good,
+ Issues,
+ Maintenance,
+ Offline
+}
+
+// number for what node, number for status
+interface NodeProps {
+ nodeNum: number;
+ statusNum: NodeStatus;
+ deployedNum: number;
+ avgCPU: number;
+ currentCPU: number;
+ avgRAM: number;
+ currentRAM: number;
+ drive1: number;
+ drive2: number;
+ uptime: number;
+}
+
+const Node = (props: NodeProps) => {
+ if (props.statusNum !== NodeStatus.Offline) {
+ return (
+
+
+ Status:
+ {props.statusNum === NodeStatus.Good &&
+
+ }
+ {props.statusNum === NodeStatus.Issues &&
+
+
+
+
+ }
+ {props.statusNum === NodeStatus.Maintenance &&
+
+ }
+
+
+ Node {props.nodeNum}
+
+
+ Current Deployed Labs: {props.deployedNum}
+
Average CPU Usage: {props.avgCPU}%
+
Current CPU Usage: {props.currentCPU}%
+
Average RAM Usage: {props.avgRAM}
+
Current RAM Usage: {props.currentRAM}
+
Storage:
+
Drive 1: {props.drive1}, Drive 2: {props.drive2}
+
Scheduled Maintenance:
+
Uptime: {props.uptime}
+
+
+
+ );
+ }
+ else {
+ return (
+
+ Status:
+
+
+
+ Node {props.nodeNum} Status:
+
+
+
+ Server Offline!
Last known ip is
+
+
+
+
+ );
+ }
+};
+
+export default Node;
diff --git a/yarn.lock b/yarn.lock
index 8b0022cb..bf49bb21 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9265,6 +9265,11 @@ mkdirp@^0.5.3, mkdirp@^0.5.4:
dependencies:
minimist "^1.2.5"
+moment@^2.29.1:
+ version "2.29.1"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
+ integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
+
moo@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
@@ -11153,6 +11158,15 @@ prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2,
object-assign "^4.1.1"
react-is "^16.8.1"
+prop-types@^15.5.7:
+ version "15.8.1"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
+ integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.13.1"
+
property-expr@^1.5.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-1.5.1.tgz#22e8706894a0c8e28d58735804f6ba3a3673314f"
@@ -11399,6 +11413,13 @@ react-copy-to-clipboard@^5.0.3:
copy-to-clipboard "^3"
prop-types "^15.5.8"
+react-datetime@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/react-datetime/-/react-datetime-3.1.1.tgz#adc346efda47653cfff9259c979f03f56106e48c"
+ integrity sha512-gHCTjAniCcMb6jdXpz+MpVe/uCeaHNDOofg+l41nLlJI3uBLBMV40CQbGB2TCTUpCzGT1mCs4vQzKGMjXO/WWQ==
+ dependencies:
+ prop-types "^15.5.7"
+
react-dev-utils@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-10.1.0.tgz#ccf82135f6dc2fc91969bc729ce57a69d8e86025"
@@ -11501,6 +11522,11 @@ react-is@^16.12.0, react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0, react-i
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==
+react-is@^16.13.1:
+ version "16.13.1"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+ integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"