-
+
{host.display_name}

@@ -258,7 +258,7 @@ const WelcomeHost = ({
return null;
})}
{host.policies?.length > 3 && (
-
+
Go to Host details to see all policies

diff --git a/frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx b/frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx
index 357534a4b8..800bbce75e 100644
--- a/frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx
+++ b/frontend/pages/hosts/ManageHostsPage/HostTableConfig.tsx
@@ -150,7 +150,7 @@ const allHostTableHeaders: IDataColumn[] = [
Cell: (cellProps: ICellProps) => (
{
return `${hostId ? `?host_ids=${hostId}` : ""}`;
@@ -92,6 +101,7 @@ const TAGGED_TEMPLATES = {
const HostDetailsPage = ({
router,
+ location: { pathname },
params: { host_id },
}: IHostDetailsProps): JSX.Element => {
const hostIdFromURL = parseInt(host_id, 10);
@@ -548,6 +558,48 @@ const HostDetailsPage = ({
const statusClassName = classnames("status", `status--${host?.status}`);
const failingPoliciesCount = host?.issues.failing_policies_count || 0;
+ const hostDetailsSubNav: IHostDetailsSubNavItem[] = [
+ {
+ name: "Details",
+ title: "details",
+ pathname: PATHS.HOST_DETAILS(hostIdFromURL),
+ },
+ {
+ name: "Software",
+ title: "software",
+ pathname: PATHS.HOST_SOFTWARE(hostIdFromURL),
+ },
+ {
+ name: "Schedule",
+ title: "schedule",
+ pathname: PATHS.HOST_SCHEDULE(hostIdFromURL),
+ },
+ {
+ name: (
+ <>
+ {failingPoliciesCount > 0 && (
+ {failingPoliciesCount}
+ )}
+ Policies
+ >
+ ),
+ title: "policies",
+ pathname: PATHS.HOST_POLICIES(hostIdFromURL),
+ },
+ ];
+
+ const getTabIndex = (path: string): number => {
+ return hostDetailsSubNav.findIndex((navItem) => {
+ // tab stays highlighted for paths that ends with same pathname
+ return path.endsWith(navItem.pathname);
+ });
+ };
+
+ const navigateToNav = (i: number): void => {
+ const navPath = hostDetailsSubNav[i].pathname;
+ router.push(navPath);
+ };
+
return (
@@ -566,17 +618,16 @@ const HostDetailsPage = ({
renderActionButtons={renderActionButtons}
/>
-
+ navigateToNav(i)}
+ >
- Details
- Software
- Schedule
-
- {failingPoliciesCount > 0 && (
- {failingPoliciesCount}
- )}
- Policies
-
+ {hostDetailsSubNav.map((navItem) => {
+ // Bolding text when the tab is active causes a layout shift
+ // so we add a hidden pseudo element with the same text string
+ return {navItem.name};
+ })}
-
{showDeleteHostModal && (
setShowDeleteHostModal(false)}
diff --git a/frontend/pages/hosts/details/HostDetailsPage/_styles.scss b/frontend/pages/hosts/details/HostDetailsPage/_styles.scss
index 1fd9a6f086..d66102c79f 100644
--- a/frontend/pages/hosts/details/HostDetailsPage/_styles.scss
+++ b/frontend/pages/hosts/details/HostDetailsPage/_styles.scss
@@ -227,7 +227,8 @@
.react-tabs__tab {
padding: 6px 0px 16px 0px;
margin-right: $pad-xxlarge;
- display: inline-block;
+ display: inline-flex;
+ flex-direction: row;
}
.react-tabs__tab--selected {
diff --git a/frontend/router/index.tsx b/frontend/router/index.tsx
index e777a59b58..94783ae873 100644
--- a/frontend/router/index.tsx
+++ b/frontend/router/index.tsx
@@ -105,7 +105,7 @@ const routes = (
-
+
@@ -143,7 +143,15 @@ const routes = (
path="manage/:active_label/labels/:label_id"
component={ManageHostsPage}
/>
-
+
+
+
+
+
+
+
+
+
diff --git a/frontend/router/paths.ts b/frontend/router/paths.ts
index 412a453fca..fc902ac5d2 100644
--- a/frontend/router/paths.ts
+++ b/frontend/router/paths.ts
@@ -48,8 +48,17 @@ export default {
MANAGE_HOSTS_LABEL: (labelId: number | string): string => {
return `${URL_PREFIX}/hosts/manage/labels/${labelId}`;
},
- HOST_DETAILS: (host: IHost): string => {
- return `${URL_PREFIX}/hosts/${host.id}`;
+ HOST_DETAILS: (id: number): string => {
+ return `${URL_PREFIX}/hosts/${id}`;
+ },
+ HOST_SOFTWARE: (id: number): string => {
+ return `${URL_PREFIX}/hosts/${id}/software`;
+ },
+ HOST_SCHEDULE: (id: number): string => {
+ return `${URL_PREFIX}/hosts/${id}/schedule`;
+ },
+ HOST_POLICIES: (id: number): string => {
+ return `${URL_PREFIX}/hosts/${id}/policies`;
},
DEVICE_USER_DETAILS: (deviceAuthToken: any): string => {
return `${URL_PREFIX}/device/${deviceAuthToken}`;