From fa31a0024481bccc45206152c634bea30f2f2aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Schw=C3=B6rer?= Date: Fri, 19 Feb 2021 16:35:52 +0100 Subject: [PATCH] web: add breadcrumbs navigation --- .../Breadcrumbs/Breadcrumbs.module.scss | 26 ++++++++ .../components/Breadcrumbs/Breadcrumbs.tsx | 42 +++++++++++++ web/src/components/Hero/Hero.module.scss | 12 ---- web/src/components/Hero/Hero.tsx | 17 ----- web/src/views/ClusterWrapper.tsx | 2 + web/src/views/Dashboard/Dashboard.tsx | 12 ++-- .../views/InstanceDetail/InstanceDetail.tsx | 63 +++++++++---------- web/src/views/Instances/Instances.tsx | 8 +-- web/src/views/Jobs/Jobs.tsx | 8 +-- web/src/views/NodeDetail/NodeDetail.tsx | 31 ++++----- web/src/views/NodeList/NodeList.tsx | 8 +-- 11 files changed, 119 insertions(+), 110 deletions(-) create mode 100644 web/src/components/Breadcrumbs/Breadcrumbs.module.scss create mode 100644 web/src/components/Breadcrumbs/Breadcrumbs.tsx delete mode 100644 web/src/components/Hero/Hero.module.scss delete mode 100644 web/src/components/Hero/Hero.tsx diff --git a/web/src/components/Breadcrumbs/Breadcrumbs.module.scss b/web/src/components/Breadcrumbs/Breadcrumbs.module.scss new file mode 100644 index 0000000..610a5d2 --- /dev/null +++ b/web/src/components/Breadcrumbs/Breadcrumbs.module.scss @@ -0,0 +1,26 @@ +.root { + padding: 0.75rem 1rem; + background: var(--colorPrimaryShade); + color: var(--colorElevationLow); + + .crumbs { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0 1rem; + + .crumb { + color: inherit; + text-transform: uppercase; + font-size: 0.9em; + + &:last-child { + font-weight: bold; + } + } + + .chevron { + opacity: 0.5; + } + } +} diff --git a/web/src/components/Breadcrumbs/Breadcrumbs.tsx b/web/src/components/Breadcrumbs/Breadcrumbs.tsx new file mode 100644 index 0000000..b79310c --- /dev/null +++ b/web/src/components/Breadcrumbs/Breadcrumbs.tsx @@ -0,0 +1,42 @@ +import { faChevronRight } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React, { ReactElement } from "react"; +import { Link, useLocation } from "react-router-dom"; +import styles from "./Breadcrumbs.module.scss"; + +const Breadcrumbs = (): ReactElement => { + const location = useLocation(); + + const crumbs = location.pathname.split("/").filter((crumb) => !!crumb); + + const crumbElements = []; + let link = ""; + + for (let i = 0; i < crumbs.length; i++) { + link += `/${crumbs[i]}`; + + crumbElements.push( + + {crumbs[i]} + + ); + + if (i < crumbs.length - 1) { + crumbElements.push( + + ); + } + } + + return ( +
+ {crumbElements} +
+ ); +}; + +export default Breadcrumbs; diff --git a/web/src/components/Hero/Hero.module.scss b/web/src/components/Hero/Hero.module.scss deleted file mode 100644 index d71e6cf..0000000 --- a/web/src/components/Hero/Hero.module.scss +++ /dev/null @@ -1,12 +0,0 @@ -.hero { - display: flex; - justify-content: space-between; - align-items: center; - padding: 2rem 1rem; - background: var(--colorPrimaryShade); - color: #fff; - - .title { - font-size: 1.5rem; - } -} diff --git a/web/src/components/Hero/Hero.tsx b/web/src/components/Hero/Hero.tsx deleted file mode 100644 index 4adbead..0000000 --- a/web/src/components/Hero/Hero.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React, { PropsWithChildren, ReactElement } from "react"; -import styles from "./Hero.module.scss"; - -interface Props { - title: string; -} - -const Hero = ({ title, children }: PropsWithChildren): ReactElement => { - return ( -
-

{title}

- {children} -
- ); -}; - -export default Hero; diff --git a/web/src/views/ClusterWrapper.tsx b/web/src/views/ClusterWrapper.tsx index 7c99bc3..607d1f5 100644 --- a/web/src/views/ClusterWrapper.tsx +++ b/web/src/views/ClusterWrapper.tsx @@ -13,6 +13,7 @@ import NodeDetail from "./NodeDetail/NodeDetail"; import Instances from "./Instances/Instances"; import InstanceConsole from "./InstanceConsole/InstanceConsole"; import Jobs from "./Jobs/Jobs"; +import Breadcrumbs from "../components/Breadcrumbs/Breadcrumbs"; interface ClusterResponse { clusters: GntCluster[]; @@ -44,6 +45,7 @@ const ClusterWrapper = (): ReactElement => { {clusterExists && ( <> + ( `clusters/${clusterName}/statistics` ); @@ -55,7 +52,6 @@ function Dashboard(): ReactElement { return ( <> -
{stats.map((stat) => ( diff --git a/web/src/views/InstanceDetail/InstanceDetail.tsx b/web/src/views/InstanceDetail/InstanceDetail.tsx index ed5e049..d3c3817 100644 --- a/web/src/views/InstanceDetail/InstanceDetail.tsx +++ b/web/src/views/InstanceDetail/InstanceDetail.tsx @@ -1,18 +1,17 @@ +import { faSkullCrossbones } from "@fortawesome/free-solid-svg-icons"; import React, { ReactElement } from "react"; -import styles from "./InstanceDetail.module.scss"; import { useParams } from "react-router-dom"; -import { GntInstance } from "../../api/models"; import { useApi } from "../../api"; +import { GntInstance } from "../../api/models"; import Button from "../../components/Button/Button"; -import { faSkullCrossbones } from "@fortawesome/free-solid-svg-icons"; import Card from "../../components/cards/Card/Card"; -import Tag from "../../components/Tag/Tag"; +import ContentWrapper from "../../components/ContentWrapper/ContentWrapper"; +import InstanceConfigurator from "../../components/InstanceConfigurator/InstanceConfigurator"; import LoadingIndicator from "../../components/LoadingIndicator/LoadingIndicator"; -import { useClusterName } from "../../helpers/hooks"; import PrefixLink from "../../components/PrefixLink"; -import InstanceConfigurator from "../../components/InstanceConfigurator/InstanceConfigurator"; -import Hero from "../../components/Hero/Hero"; -import ContentWrapper from "../../components/ContentWrapper/ContentWrapper"; +import Tag from "../../components/Tag/Tag"; +import { useClusterName } from "../../helpers/hooks"; +import styles from "./InstanceDetail.module.scss"; interface InstanceResponse { instance: GntInstance; @@ -40,31 +39,29 @@ const InstanceDetail = (): ReactElement => { <> {instance && (
- -
- {instance.offersVnc && ( - -
-
+
+ {instance.offersVnc && ( + +
diff --git a/web/src/views/Instances/Instances.tsx b/web/src/views/Instances/Instances.tsx index 006d6c8..6975b21 100644 --- a/web/src/views/Instances/Instances.tsx +++ b/web/src/views/Instances/Instances.tsx @@ -4,7 +4,6 @@ import { useClusterName } from "../../helpers/hooks"; import { useApi } from "../../api"; import { GntInstance } from "../../api/models"; import LoadingIndicator from "../../components/LoadingIndicator/LoadingIndicator"; -import Hero from "../../components/Hero/Hero"; import ContentWrapper from "../../components/ContentWrapper/ContentWrapper"; interface InstancesResponse { @@ -32,12 +31,7 @@ const Instances = (): ReactElement => { return ; }; - return ( -
- - {renderContent()} -
- ); + return {renderContent()}; }; export default Instances; diff --git a/web/src/views/Jobs/Jobs.tsx b/web/src/views/Jobs/Jobs.tsx index 8a0b431..c1ba09e 100644 --- a/web/src/views/Jobs/Jobs.tsx +++ b/web/src/views/Jobs/Jobs.tsx @@ -2,7 +2,6 @@ import React, { ReactElement } from "react"; import { useApi } from "../../api"; import { GntJob } from "../../api/models"; import ContentWrapper from "../../components/ContentWrapper/ContentWrapper"; -import Hero from "../../components/Hero/Hero"; import JobList from "../../components/JobList/JobList"; import LoadingIndicator from "../../components/LoadingIndicator/LoadingIndicator"; import { useClusterName } from "../../helpers/hooks"; @@ -37,12 +36,7 @@ const Jobs = (): ReactElement => { return ; }; - return ( - <> - - {renderContent()} - - ); + return {renderContent()}; }; export default Jobs; diff --git a/web/src/views/NodeDetail/NodeDetail.tsx b/web/src/views/NodeDetail/NodeDetail.tsx index b44f99c..7ddbf12 100644 --- a/web/src/views/NodeDetail/NodeDetail.tsx +++ b/web/src/views/NodeDetail/NodeDetail.tsx @@ -4,7 +4,6 @@ import { useClusterName } from "../../helpers/hooks"; import { GntInstance, GntNode } from "../../api/models"; import { useApi } from "../../api"; import LoadingIndicator from "../../components/LoadingIndicator/LoadingIndicator"; -import Hero from "../../components/Hero/Hero"; import InstanceList from "../../components/InstanceList/InstanceList"; import ContentWrapper from "../../components/ContentWrapper/ContentWrapper"; import styles from "./NodeDetail.module.scss"; @@ -31,26 +30,20 @@ const NodeDetail = (): ReactElement => { return
Failed to load: {error}
; } - const { node } = data; - return ( - <> - - - -
-
-

Primary Instances

- -
- -
-

Secondary Instances

- -
+ +
+
+

Primary Instances

+ +
+ +
+

Secondary Instances

+
- - +
+
); }; diff --git a/web/src/views/NodeList/NodeList.tsx b/web/src/views/NodeList/NodeList.tsx index b318c54..39cd104 100644 --- a/web/src/views/NodeList/NodeList.tsx +++ b/web/src/views/NodeList/NodeList.tsx @@ -8,7 +8,6 @@ import MemoryUtilisation from "../../components/MemoryUtilisation/MemoryUtilisat import LoadingIndicator from "../../components/LoadingIndicator/LoadingIndicator"; import { useClusterName } from "../../helpers/hooks"; import PrefixLink from "../../components/PrefixLink"; -import Hero from "../../components/Hero/Hero"; import ContentWrapper from "../../components/ContentWrapper/ContentWrapper"; interface NodesResponse { @@ -74,12 +73,7 @@ function NodeList(): ReactElement { ); }; - return ( - <> - - {renderContent()} - - ); + return {renderContent()}; } export default NodeList;