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"