Skip to content

Commit

Permalink
web: add breadcrumbs navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
paulschwoerer committed Feb 19, 2021
1 parent 8a16184 commit fa31a00
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 110 deletions.
26 changes: 26 additions & 0 deletions web/src/components/Breadcrumbs/Breadcrumbs.module.scss
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
42 changes: 42 additions & 0 deletions web/src/components/Breadcrumbs/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
@@ -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(
<Link to={link} key={i} className={styles.crumb}>
{crumbs[i]}
</Link>
);

if (i < crumbs.length - 1) {
crumbElements.push(
<FontAwesomeIcon
key={`chevron-${i}`}
className={styles.chevron}
icon={faChevronRight}
/>
);
}
}

return (
<div className={styles.root}>
<span className={styles.crumbs}>{crumbElements}</span>
</div>
);
};

export default Breadcrumbs;
12 changes: 0 additions & 12 deletions web/src/components/Hero/Hero.module.scss

This file was deleted.

17 changes: 0 additions & 17 deletions web/src/components/Hero/Hero.tsx

This file was deleted.

2 changes: 2 additions & 0 deletions web/src/views/ClusterWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down Expand Up @@ -44,6 +45,7 @@ const ClusterWrapper = (): ReactElement => {
{clusterExists && (
<>
<Navbar clusters={clusterData.clusters} />
<Breadcrumbs />
<Switch>
<AuthenticatedRoute
exact
Expand Down
12 changes: 4 additions & 8 deletions web/src/views/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React, { ReactElement, useContext } from "react";
import styles from "./Dashboard.module.scss";
import React, { ReactElement } from "react";
import { useApi } from "../../api";
import Card from "../../components/cards/Card/Card";
import ContentWrapper from "../../components/ContentWrapper/ContentWrapper";
import AuthContext from "../../api/AuthContext";
import Hero from "../../components/Hero/Hero";
import { useApi } from "../../api";
import { useClusterName } from "../../helpers/hooks";
import LoadingIndicator from "../../components/LoadingIndicator/LoadingIndicator";
import { convertMBToGB } from "../../helpers";
import { useClusterName } from "../../helpers/hooks";
import styles from "./Dashboard.module.scss";

interface StatisticElement {
count: number;
Expand All @@ -21,7 +19,6 @@ interface StatisticsResponse {

function Dashboard(): ReactElement {
const clusterName = useClusterName();
const authContext = useContext(AuthContext);
const [{ data, isLoading, error }] = useApi<StatisticsResponse>(
`clusters/${clusterName}/statistics`
);
Expand Down Expand Up @@ -55,7 +52,6 @@ function Dashboard(): ReactElement {

return (
<>
<Hero title={`Welcome back, ${authContext.username}!`} />
<ContentWrapper>
<div className={styles.statistics}>
{stats.map((stat) => (
Expand Down
63 changes: 30 additions & 33 deletions web/src/views/InstanceDetail/InstanceDetail.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -40,31 +39,29 @@ const InstanceDetail = (): ReactElement => {
<>
{instance && (
<div>
<Hero title={instance.name}>
<div className={styles.actions}>
{instance.offersVnc && (
<PrefixLink
className={styles.link}
to={`/instances/${instance.name}/console`}
>
<Button
className={styles.action}
label="Open Console"
primary
/>
</PrefixLink>
)}
<Button className={styles.action} label="Migrate" primary />
<Button className={styles.action} label="Failover" primary />
<Button className={styles.action} label="Shutdown" danger />
<Button
className={styles.action}
label="Kill"
icon={faSkullCrossbones}
danger
/>
</div>
</Hero>
<div className={styles.actions}>
{instance.offersVnc && (
<PrefixLink
className={styles.link}
to={`/instances/${instance.name}/console`}
>
<Button
className={styles.action}
label="Open Console"
primary
/>
</PrefixLink>
)}
<Button className={styles.action} label="Migrate" primary />
<Button className={styles.action} label="Failover" primary />
<Button className={styles.action} label="Shutdown" danger />
<Button
className={styles.action}
label="Kill"
icon={faSkullCrossbones}
danger
/>
</div>
<ContentWrapper>
<InstanceConfigurator instance={instance} />
<div className={styles.details}>
Expand Down
8 changes: 1 addition & 7 deletions web/src/views/Instances/Instances.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -32,12 +31,7 @@ const Instances = (): ReactElement => {
return <InstanceList instances={data.instances} />;
};

return (
<div>
<Hero title={`Instances on ${clusterName} cluster`} />
<ContentWrapper>{renderContent()}</ContentWrapper>
</div>
);
return <ContentWrapper>{renderContent()}</ContentWrapper>;
};

export default Instances;
8 changes: 1 addition & 7 deletions web/src/views/Jobs/Jobs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -37,12 +36,7 @@ const Jobs = (): ReactElement => {
return <JobList jobs={data.jobs} />;
};

return (
<>
<Hero title="Jobs" />
<ContentWrapper>{renderContent()}</ContentWrapper>
</>
);
return <ContentWrapper>{renderContent()}</ContentWrapper>;
};

export default Jobs;
31 changes: 12 additions & 19 deletions web/src/views/NodeDetail/NodeDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -31,26 +30,20 @@ const NodeDetail = (): ReactElement => {
return <div>Failed to load: {error}</div>;
}

const { node } = data;

return (
<>
<Hero title={node.name} />

<ContentWrapper>
<div className={styles.wrapper}>
<div>
<h2>Primary Instances</h2>
<InstanceList instances={data.primaryInstances} />
</div>

<div>
<h2>Secondary Instances</h2>
<InstanceList instances={data.secondaryInstances} />
</div>
<ContentWrapper>
<div className={styles.wrapper}>
<div>
<h2>Primary Instances</h2>
<InstanceList instances={data.primaryInstances} />
</div>

<div>
<h2>Secondary Instances</h2>
<InstanceList instances={data.secondaryInstances} />
</div>
</ContentWrapper>
</>
</div>
</ContentWrapper>
);
};

Expand Down
8 changes: 1 addition & 7 deletions web/src/views/NodeList/NodeList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -74,12 +73,7 @@ function NodeList(): ReactElement {
);
};

return (
<>
<Hero title={`Nodes on ${clusterName} cluster`} />
<ContentWrapper>{renderContent()}</ContentWrapper>
</>
);
return <ContentWrapper>{renderContent()}</ContentWrapper>;
}

export default NodeList;

0 comments on commit fa31a00

Please sign in to comment.