mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-12 12:33:02 +00:00
Server details in server dashboard
This commit is contained in:
@@ -37,6 +37,15 @@ object-assign
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react.production.min.js
|
||||
*
|
||||
|
@@ -43,10 +43,11 @@
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^17.0.1",
|
||||
"react-bootstrap": "^1.4.0",
|
||||
"react-bootstrap": "^2.1.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-icons": "^4.1.0",
|
||||
"react-multi-select-component": "^3.0.7",
|
||||
"react-object-table-viewer": "^1.0.7",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
@@ -60,7 +61,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.5",
|
||||
"sinon": "^13.0.1",
|
||||
"babel-jest": "^26.6.3",
|
||||
"enzyme": "^3.11.0",
|
||||
"eslint": "^7.18.0",
|
||||
@@ -68,6 +68,7 @@
|
||||
"eslint-plugin-react": "^7.22.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^26.6.3",
|
||||
"prettier": "^2.2.1"
|
||||
"prettier": "^2.2.1",
|
||||
"sinon": "^13.0.1"
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,8 @@ import { useSelector, useDispatch } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { Button, Col, Row, FormControl } from "react-bootstrap";
|
||||
import ReactObjectTableViewer from "react-object-table-viewer";
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa";
|
||||
|
||||
@@ -19,6 +21,15 @@ const AccessServerButton = ({ url }) => (
|
||||
</a>
|
||||
);
|
||||
|
||||
const ToggleButton = ({ dataTarget }) => {
|
||||
var [errorAlert, setErrorAlert] = useState(null);
|
||||
return (
|
||||
<button data-toggle="collapse" data-target={dataTarget}>
|
||||
<span className="caret"></span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const ServerDashboard = (props) => {
|
||||
let base_url = window.base_url;
|
||||
// sort methods
|
||||
@@ -189,6 +200,91 @@ const ServerDashboard = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const serverRow = (user, server) => {
|
||||
const { servers, ...userNoServers } = user;
|
||||
return [
|
||||
<tr key={`${user.name}-${server.name}-row`} className="user-row">
|
||||
<td data-testid="user-row-name">
|
||||
<span>
|
||||
<ToggleButton
|
||||
dataTarget={`#${user.name}-${server.name}-collapse`}
|
||||
/>{" "}
|
||||
</span>
|
||||
{user.name}
|
||||
</td>
|
||||
<td data-testid="user-row-admin">{user.admin ? "admin" : ""}</td>
|
||||
|
||||
<td data-testid="user-row-server">
|
||||
{server.name ? (
|
||||
<p class="text-secondary">{server.name}</p>
|
||||
) : (
|
||||
<p style={{ color: "lightgrey" }}>[MAIN]</p>
|
||||
)}
|
||||
</td>
|
||||
<td data-testid="user-row-last-activity">
|
||||
{server.last_activity ? timeSince(server.last_activity) : "Never"}
|
||||
</td>
|
||||
<td data-testid="user-row-server-activity">
|
||||
{server.started ? (
|
||||
// Stop Single-user server
|
||||
<>
|
||||
<StopServerButton serverName={server.name} userName={user.name} />
|
||||
<AccessServerButton url={server.url} />
|
||||
</>
|
||||
) : (
|
||||
// Start Single-user server
|
||||
<>
|
||||
<StartServerButton
|
||||
serverName={server.name}
|
||||
userName={user.name}
|
||||
style={{ marginRight: 20 }}
|
||||
/>
|
||||
<a
|
||||
href={`${base_url}spawn/${user.name}${
|
||||
server.name && "/" + server.name
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
className="btn btn-secondary btn-xs"
|
||||
style={{ marginRight: 20 }}
|
||||
>
|
||||
Spawn Page
|
||||
</button>
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
<EditUserCell user={user} />
|
||||
</tr>,
|
||||
<tr>
|
||||
<td colSpan={6} style={{ padding: 0 }}>
|
||||
<div
|
||||
id={`${user.name}-${server.name}-collapse`}
|
||||
className="collapse"
|
||||
colSpan={6}
|
||||
>
|
||||
<div style={{ padding: 6 }}>
|
||||
<ReactObjectTableViewer
|
||||
className="table-striped table-bordered admin-table-head"
|
||||
style={{
|
||||
padding: "3px 6px",
|
||||
margin: "auto",
|
||||
}}
|
||||
keyStyle={{
|
||||
padding: "4px",
|
||||
}}
|
||||
valueStyle={{
|
||||
padding: "4px",
|
||||
}}
|
||||
data={{ user: userNoServers, server: server }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>,
|
||||
];
|
||||
};
|
||||
|
||||
let servers = user_data.flatMap((user) => {
|
||||
let userServers = Object.values({
|
||||
"": user.server || {},
|
||||
@@ -234,7 +330,7 @@ const ServerDashboard = (props) => {
|
||||
<Link to="/groups">{"> Manage Groups"}</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
<table className="table table-striped table-bordered table-hover">
|
||||
<table className="table table-bordered table-hover">
|
||||
<thead className="admin-table-head">
|
||||
<tr>
|
||||
<th id="user-header">
|
||||
@@ -373,63 +469,7 @@ const ServerDashboard = (props) => {
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
{servers.map(([user, server], i) => {
|
||||
server.name = server.name || "";
|
||||
return (
|
||||
<tr key={i + "row"} className="user-row">
|
||||
<td data-testid="user-row-name">{user.name}</td>
|
||||
<td data-testid="user-row-admin">
|
||||
{user.admin ? "admin" : ""}
|
||||
</td>
|
||||
|
||||
<td data-testid="user-row-server">
|
||||
{server.name ? (
|
||||
<p class="text-secondary">{server.name}</p>
|
||||
) : (
|
||||
<p style={{ color: "lightgrey" }}>[MAIN]</p>
|
||||
)}
|
||||
</td>
|
||||
<td data-testid="user-row-last-activity">
|
||||
{server.last_activity
|
||||
? timeSince(server.last_activity)
|
||||
: "Never"}
|
||||
</td>
|
||||
<td data-testid="user-row-server-activity">
|
||||
{server.started ? (
|
||||
// Stop Single-user server
|
||||
<>
|
||||
<StopServerButton
|
||||
serverName={server.name}
|
||||
userName={user.name}
|
||||
/>
|
||||
<AccessServerButton url={server.url} />
|
||||
</>
|
||||
) : (
|
||||
// Start Single-user server
|
||||
<>
|
||||
<StartServerButton
|
||||
serverName={server.name}
|
||||
userName={user.name}
|
||||
/>
|
||||
<a
|
||||
href={`${base_url}spawn/${user.name}${
|
||||
server.name && "/" + server.name
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
className="btn btn-secondary btn-xs"
|
||||
style={{ marginRight: 20 }}
|
||||
>
|
||||
Spawn Page
|
||||
</button>
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
<EditUserCell user={user} />
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
{servers.flatMap(([user, server]) => serverRow(user, server))}
|
||||
</tbody>
|
||||
</table>
|
||||
<PaginationFooter
|
||||
|
@@ -928,7 +928,7 @@
|
||||
"@babel/plugin-transform-react-jsx-development" "^7.16.7"
|
||||
"@babel/plugin-transform-react-pure-annotations" "^7.16.7"
|
||||
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.8", "@babel/runtime@^7.14.0", "@babel/runtime@^7.15.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.16", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
|
||||
version "7.17.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825"
|
||||
integrity sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==
|
||||
@@ -1215,23 +1215,41 @@
|
||||
"@jridgewell/resolve-uri" "^3.0.3"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
|
||||
"@popperjs/core@^2.8.6":
|
||||
"@popperjs/core@^2.10.1":
|
||||
version "2.11.4"
|
||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.4.tgz#d8c7b8db9226d2d7664553a0741ad7d0397ee503"
|
||||
integrity sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg==
|
||||
|
||||
"@restart/context@^2.1.4":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@restart/context/-/context-2.1.4.tgz#a99d87c299a34c28bd85bb489cb07bfd23149c02"
|
||||
integrity sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q==
|
||||
"@react-aria/ssr@^3.0.1":
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.1.2.tgz#665a6fd56385068c7417922af2d0d71b0618e52d"
|
||||
integrity sha512-amXY11ImpokvkTMeKRHjsSsG7v1yzzs6yeqArCyBIk60J3Yhgxwx9Cah+Uu/804ATFwqzN22AXIo7SdtIaMP+g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
|
||||
"@restart/hooks@^0.3.26":
|
||||
version "0.3.27"
|
||||
resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.3.27.tgz#91f356d66d4699a8cd8b3d008402708b6a9dc505"
|
||||
integrity sha512-s984xV/EapUIfkjlf8wz9weP2O9TNKR96C68FfMEy2bE69+H4cNv3RD4Mf97lW7Htt7PjZrYTjSC8f3SB9VCXw==
|
||||
"@restart/hooks@^0.4.0", "@restart/hooks@^0.4.5":
|
||||
version "0.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.5.tgz#e7acbea237bfc9e479970500cf87538b41a1ed02"
|
||||
integrity sha512-tLGtY0aHeIfT7aPwUkvQuhIy3+q3w4iqmUzFLPlOAf/vNUacLaBt1j/S//jv/dQhenRh8jvswyMojCwmLvJw8A==
|
||||
dependencies:
|
||||
dequal "^2.0.2"
|
||||
|
||||
"@restart/ui@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@restart/ui/-/ui-1.0.2.tgz#009a06ae698d624672c5e6a776efd0e8a6017842"
|
||||
integrity sha512-vKGe0UBJLnbvNAjr8ljlDvphf2HkpjBjXsblmgKPvKdZBDn/mtAz89wmznaomIaEJ9VNoSEY0vA5T5MDi2jIcQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.16"
|
||||
"@popperjs/core" "^2.10.1"
|
||||
"@react-aria/ssr" "^3.0.1"
|
||||
"@restart/hooks" "^0.4.0"
|
||||
"@types/warning" "^3.0.0"
|
||||
dequal "^2.0.2"
|
||||
dom-helpers "^5.2.0"
|
||||
prop-types "^15.7.2"
|
||||
uncontrollable "^7.2.1"
|
||||
warning "^4.0.3"
|
||||
|
||||
"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3":
|
||||
version "1.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
|
||||
@@ -1399,7 +1417,7 @@
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
|
||||
"@types/invariant@^2.2.33":
|
||||
"@types/invariant@^2.2.35":
|
||||
version "2.2.35"
|
||||
resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.35.tgz#cd3ebf581a6557452735688d8daba6cf0bd5a3be"
|
||||
integrity sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==
|
||||
@@ -1456,7 +1474,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17"
|
||||
integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==
|
||||
|
||||
"@types/prop-types@*", "@types/prop-types@^15.7.3":
|
||||
"@types/prop-types@*", "@types/prop-types@^15.7.4":
|
||||
version "15.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
|
||||
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
|
||||
@@ -1478,7 +1496,7 @@
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
redux "^4.0.0"
|
||||
|
||||
"@types/react-transition-group@^4.4.1":
|
||||
"@types/react-transition-group@^4.4.4":
|
||||
version "4.4.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.4.tgz#acd4cceaa2be6b757db61ed7b432e103242d163e"
|
||||
integrity sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==
|
||||
@@ -6167,30 +6185,29 @@ raw-body@2.4.3:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
react-bootstrap@^1.4.0:
|
||||
version "1.6.4"
|
||||
resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-1.6.4.tgz#94d5d2422e26bba277656d3529128e14f838b7ca"
|
||||
integrity sha512-z3BhBD4bEZuLP8VrYqAD7OT7axdcSkkyvWBWnS2U/4MhyabUihrUyucPWkan7aMI1XIHbmH4LCpEtzWGfx/yfA==
|
||||
react-bootstrap@^2.1.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-2.2.1.tgz#2a6ad0931e9367882ec3fc88a70ed0b8ace90b26"
|
||||
integrity sha512-x8lpVQflsbevphuWbTnTNCatcbKyPJNrP2WyQ1MJYmFEcVjbTbai1yZhdlXr0QUxLQLxA8g5hQWb5TwJtaZoCA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.14.0"
|
||||
"@restart/context" "^2.1.4"
|
||||
"@restart/hooks" "^0.3.26"
|
||||
"@types/invariant" "^2.2.33"
|
||||
"@types/prop-types" "^15.7.3"
|
||||
"@babel/runtime" "^7.17.2"
|
||||
"@restart/hooks" "^0.4.5"
|
||||
"@restart/ui" "^1.0.2"
|
||||
"@types/invariant" "^2.2.35"
|
||||
"@types/prop-types" "^15.7.4"
|
||||
"@types/react" ">=16.14.8"
|
||||
"@types/react-transition-group" "^4.4.1"
|
||||
"@types/react-transition-group" "^4.4.4"
|
||||
"@types/warning" "^3.0.0"
|
||||
classnames "^2.3.1"
|
||||
dom-helpers "^5.2.1"
|
||||
invariant "^2.2.4"
|
||||
prop-types "^15.7.2"
|
||||
prop-types "^15.8.1"
|
||||
prop-types-extra "^1.1.0"
|
||||
react-overlays "^5.1.1"
|
||||
react-transition-group "^4.4.1"
|
||||
react-transition-group "^4.4.2"
|
||||
uncontrollable "^7.2.1"
|
||||
warning "^4.0.3"
|
||||
|
||||
react-dom@^17.0.1:
|
||||
react-dom@17.0.2, react-dom@^17.0.1:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
||||
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
|
||||
@@ -6226,19 +6243,13 @@ react-multi-select-component@^3.0.7:
|
||||
dependencies:
|
||||
goober "^2.0.30"
|
||||
|
||||
react-overlays@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-5.1.1.tgz#2e7cf49744b56537c7828ccb94cfc63dd778ae4f"
|
||||
integrity sha512-eCN2s2/+GVZzpnId4XVWtvDPYYBD2EtOGP74hE+8yDskPzFy9+pV1H3ZZihxuRdEbQzzacySaaDkR7xE0ydl4Q==
|
||||
react-object-table-viewer@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/react-object-table-viewer/-/react-object-table-viewer-1.0.7.tgz#31816021fa4526641c6b66bd9433ec9b78c2e472"
|
||||
integrity sha512-OezCet8+BmEdJJHO5WGPFPRWXxw4Ls6HsV4Uh1kRPlmRXLOTNqWt/ZHmH8NhTl1BA9HkdhEegKVqc2b61wDMLg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.8"
|
||||
"@popperjs/core" "^2.8.6"
|
||||
"@restart/hooks" "^0.3.26"
|
||||
"@types/warning" "^3.0.0"
|
||||
dom-helpers "^5.2.0"
|
||||
prop-types "^15.7.2"
|
||||
uncontrollable "^7.2.1"
|
||||
warning "^4.0.3"
|
||||
react "^17.0.2"
|
||||
react-dom "17.0.2"
|
||||
|
||||
react-redux@^7.2.2:
|
||||
version "7.2.6"
|
||||
@@ -6299,7 +6310,7 @@ react-test-renderer@^17.0.0:
|
||||
react-shallow-renderer "^16.13.1"
|
||||
scheduler "^0.20.2"
|
||||
|
||||
react-transition-group@^4.4.1:
|
||||
react-transition-group@^4.4.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
|
||||
integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==
|
||||
@@ -6309,7 +6320,7 @@ react-transition-group@^4.4.1:
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react@^17.0.1:
|
||||
react@^17.0.1, react@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
||||
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user