Skip to content

Commit be9c32c

Browse files
authored
Merge pull request #76 from unity-sds/bugs/75-fix-navigation-menu
Bugs/75 fix navigation menu
2 parents 9a2ff16 + a55de82 commit be9c32c

File tree

9 files changed

+119
-74
lines changed

9 files changed

+119
-74
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- Fixed issue with basepath not set correctly after proxy label was updated from `ui` to `portal` [#68](https://github.com/unity-sds/unity-portal/issues/68)
1616
- Changed consumption of Health API endpoint so that it now captures information for three new fields, `componentCategory`, `componentType`, and `description` [#71](https://github.com/unity-sds/unity-portal/issues/71)
1717
- Updated home page view so that it only lists items that have a `componentType` of `ui` [#71](https://github.com/unity-sds/unity-portal/issues/71)
18+
- Fixed inconsistent routes being displayed when comparing the home page cards with what was shown in the main navigations application's menu [#75](https://github.com/unity-sds/unity-portal/issues/75)
1819

1920
## [0.8.0] 2025-01-14
2021
- Added support to report on when health API endpoint is not available in navbar and on health dashboard

src/Config.tsx

+61-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,35 @@
1-
const Config = {
1+
import { Service } from "./state/slices/healthSlice";
2+
3+
type Config = {
4+
general: {
5+
appTitle:string,
6+
version:string,
7+
adminEmail:string,
8+
wwwDomain:string,
9+
basePath:string,
10+
project:string,
11+
venue:string,
12+
defaultRoutes:Service[]
13+
},
14+
auth: {
15+
oauthRedirectUri:string,
16+
oauthLogoutEndpoint:string,
17+
oauthProviderUrl:string,
18+
appAdminGroupName:string,
19+
appViewerGroupName:string,
20+
},
21+
cs: {
22+
healthEndpointUrl:string
23+
},
24+
ads: {
25+
url:string
26+
},
27+
sps: {
28+
endpoint:string
29+
}
30+
}
31+
32+
const Config:Config = {
233

334
['general']: {
435
appTitle: "MDPS",
@@ -7,7 +38,35 @@ const Config = {
738
wwwDomain: import.meta.env.VITE_WWW_DOMAIN,
839
basePath: import.meta.env.VITE_BASE_PATH,
940
project: import.meta.env.VITE_PROJECT,
10-
venue: import.meta.env.VITE_VENUE
41+
venue: import.meta.env.VITE_VENUE,
42+
defaultRoutes: [
43+
{
44+
componentName: "Health Dashboard",
45+
componentCategory: "",
46+
componentType: "ui",
47+
description: "Check the health status of services running in this venue.",
48+
healthChecks: [],
49+
healthCheckUrl: "",
50+
landingPageUrl: "/health-dashboard",
51+
nativeRoute: true,
52+
reportHealthStatus: false,
53+
route: "/health-dashboard",
54+
ssmKey: ""
55+
},
56+
{
57+
componentName: "Documentation (Gitbook)",
58+
componentCategory: "",
59+
componentType: "ui",
60+
description: "Documentation to help become familiar with the Unity platform.",
61+
healthChecks: [],
62+
healthCheckUrl: "",
63+
landingPageUrl: "https://unity-sds.gitbook.io/docs",
64+
nativeRoute: true,
65+
reportHealthStatus: false,
66+
route: "https://unity-sds.gitbook.io/docs",
67+
ssmKey: ""
68+
}
69+
]
1170
},
1271

1372
['auth']: {

src/Root.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import NotFound from "./routes/errors/not-found";
1414
import { useAppDispatch, useAppSelector } from "./state/hooks";
1515
import { useEffect, useState } from "react";
1616
import { getHealthData } from "./state/slices/healthSlice";
17-
import { formatRoute } from "./utils/strings";
1817
import { Progress } from "@nasa-jpl/react-stellar";
1918

2019
function Root() {
@@ -69,7 +68,8 @@ function Root() {
6968
<Routes>
7069
{
7170
healthState.items.map( (item, index) => {
72-
return <Route key={index} path={"/applications/" + formatRoute(item.componentName)} element={<WebView url={item.landingPageUrl} />} />
71+
if( !item.nativeRoute )
72+
return <Route key={index} path={item.route} element={<WebView url={item.landingPageUrl} />} />
7373
})
7474
}
7575
<Route path="/" element={<Navigate to="/home" replace={true} />} />

src/components/Navbar/index.tsx

+17-30
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ import {
1717
NavbarContent,
1818
NavbarMobileMenu
1919
} from "@nasa-jpl/react-stellar";
20-
import { getHealthData } from "../../state/slices/healthSlice";
2120
import { GetUsername } from "../../AuthorizationWrapper";
2221
import { logout } from "../../utils/auth";
23-
import { useAppDispatch, useAppSelector } from "../../state/hooks";
22+
import { useAppSelector } from "../../state/hooks";
2423
import { useEffect, useState, } from "react";
24+
import { getUiItems } from "../../state/selectors/healthSelectors";
25+
import { Service } from "../../state/slices/healthSlice";
2526
import MdpsLogo from "../../assets/images/mdps-logo.svg";
2627

2728
import Config from "../../Config";
28-
import { formatRoute } from "../../utils/strings";
2929

3030
const MenuErrorMessage = ({message}:{message:string}) => {
3131
return <div className="st-react-menu-message"><IconWarning />{message}</div>
@@ -35,8 +35,6 @@ export default function Navbar() {
3535

3636
const [healthApiError, setHealthApiError] = useState(false);
3737
const healthApiErrorMessage = "Application List Unavailable";
38-
39-
const dispatch = useAppDispatch();
4038

4139
const loggedInUsername = GetUsername();
4240
const userInitials = loggedInUsername.substring(0,1).toUpperCase();
@@ -50,21 +48,20 @@ export default function Navbar() {
5048
return state.health;
5149
});
5250

51+
const uiItems:Service[] = useAppSelector((state) => {
52+
return getUiItems(state.health);
53+
});
54+
5355
useEffect(() => {
5456

55-
if (healthState.status === "idle") {
56-
// Fetch the health data
57-
dispatch(getHealthData());
58-
} else if ( healthState.status === "pending" ) {
59-
// Do something to inform the user that the health data is being fetched
60-
} else if (healthState.status === "succeeded") {
61-
// Do something to handle the successful fetching of data
62-
} else if (healthState.status === "failed") {
57+
if (healthState.status === "failed") {
6358
// Do something to handle the error
6459
setHealthApiError(true);
6560
}
6661

67-
}, [dispatch, healthState]);
62+
}, [healthState]);
63+
64+
6865

6966
return (
7067
<StellarNavbar mobileBreakpoint={800}>
@@ -96,17 +93,13 @@ export default function Navbar() {
9693
</Button>
9794
}>
9895
<NavLink to="/"><MenuItem>Home</MenuItem></NavLink>
99-
<NavLink to="/health-dashboard"><MenuItem>Health Dashboard</MenuItem></NavLink>
10096
{
101-
healthState.items.map( (service, index) => {
102-
return <NavLink to={"/applications/" + formatRoute(service.componentName)} key={index}>
97+
uiItems.map( (service, index) => {
98+
return <NavLink to={service.route} key={index}>
10399
<MenuItem>{service.componentName}</MenuItem>
104100
</NavLink>
105101
})
106102
}
107-
<NavLink to="https://unity-sds.gitbook.io/docs" target="_blank">
108-
<MenuItem>Documentation (Gitbook)</MenuItem>
109-
</NavLink>
110103
{
111104
healthApiError && <MenuErrorMessage message={healthApiErrorMessage} />
112105
}
@@ -162,17 +155,13 @@ export default function Navbar() {
162155
</Button>
163156
}>
164157
<NavLink to="/"><MenuItem>Home</MenuItem></NavLink>
165-
<NavLink to="/health-dashboard"><MenuItem>Health Dashboard</MenuItem></NavLink>
166158
{
167-
healthState.items.map( (service, index) => {
168-
return <NavLink to={"/applications/" + formatRoute(service.componentName)} key={index}>
159+
uiItems.map( (service, index) => {
160+
return <NavLink to={service.route} key={index}>
169161
<MenuItem>{service.componentName}</MenuItem>
170162
</NavLink>
171163
})
172164
}
173-
<NavLink to="https://unity-sds.gitbook.io/docs" target="_blank">
174-
<MenuItem>Documentation (Gitbook)</MenuItem>
175-
</NavLink>
176165
{
177166
healthApiError && <MenuErrorMessage message={healthApiErrorMessage} />
178167
}
@@ -237,13 +226,11 @@ export default function Navbar() {
237226
</NavbarBreakpoint>
238227
<NavbarMobileMenu>
239228
<NavLink to="/" className="st-react-navbar-link"><IconHome />{' '}Home</NavLink>
240-
<NavLink to="/health-dashboard" className="st-react-navbar-link">{' '}Health Dashboard</NavLink>
241229
{
242-
healthState.items.map( (service, index) => {
243-
return <NavLink key={index} className="st-react-navbar-link" to={"/applications/" + formatRoute(service.componentName)}>{service.componentName}</NavLink>
230+
uiItems.map( (service, index) => {
231+
return <NavLink key={index} className="st-react-navbar-link" to={service.route}>{service.componentName}</NavLink>
244232
})
245233
}
246-
<NavLink to="https://unity-sds.gitbook.io/docs" target="_blank" className="st-react-navbar-link">{' '}Documentation (Gitbook)</NavLink>
247234
{
248235
healthApiError && <MenuErrorMessage message={healthApiErrorMessage} />
249236
}

src/routes/health-dashboard/index.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { useAppDispatch, useAppSelector } from "../../state/hooks";
77
import { IconWarning, Error } from "@nasa-jpl/react-stellar";
88
import React from "react";
99
import { Link } from "react-router-dom";
10+
import { Service } from "../../state/slices/healthSlice";
1011

1112
const LinkCellRenderer = (props:CustomCellRendererProps) => {
1213
return <Link to={props.value} target="_blank">{props.value}</Link>
@@ -74,6 +75,12 @@ function HealthDashboard() {
7475
[]
7576
);
7677

78+
const getHealthDashboardData = () => {
79+
return healthState.items.filter( (service:Service) => {
80+
return service.reportHealthStatus ? true : false;
81+
})
82+
}
83+
7784
// Example of consuming Grid Event
7885
const cellClickedListener = useCallback( (event:CellClickedEvent) => {
7986

@@ -129,7 +136,7 @@ function HealthDashboard() {
129136
{ healthApiError && <Error><IconWarning />{healthApiErrorMessage}</Error> }
130137
<div className="ag-theme-stellar mdps-aggrid-container">
131138
<AgGridReact
132-
rowData={healthState.items} // Row Data for Rows
139+
rowData={getHealthDashboardData()} // Row Data for Rows
133140
columnDefs={columnDefs} // Column Defs for Columns
134141
defaultColDef={defaultColDef} // Default Column Properties
135142
animateRows={true} // Optional - set to 'true' to have rows animate when sorted

src/routes/home/index.tsx

+2-30
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,25 @@ import { DocumentMeta } from "../../components/DocumentMeta/DocumentMeta";
44
import { useAppSelector } from "../../state/hooks";
55
import { getUiItems } from "../../state/selectors/healthSelectors";
66
import { Service } from "../../state/slices/healthSlice";
7-
import { formatRoute } from "../../utils/strings";
87

98
function Home() {
109

1110
const uiItems:Service[] = useAppSelector((state) => {
1211
return getUiItems(state.health);
1312
});
1413

15-
let appCards = uiItems.map( (item) => {
14+
const appCards = uiItems.map( (item) => {
1615
return (
1716
<Card
1817
description={item.description}
19-
route={"/applications/" + formatRoute(item.componentName)}
18+
route={item.route}
2019
title={item.componentName}
2120
type={item.componentType}
2221
url={item.landingPageUrl}
2322
/>
2423
)
2524
})
2625

27-
appCards.push(
28-
<Card
29-
description={`Check the health status of services running in this venue.`}
30-
route={"/health-dashboard"}
31-
title="Health Dashboard"
32-
type={"ui"}
33-
url={"/health-dashboard"}
34-
/>,
35-
<Card
36-
description="Documentation to help become familiar with the Unity platform."
37-
route={"https://unity-sds.gitbook.io/docs"}
38-
title="Documentation (Gitbook)"
39-
type={"ui"}
40-
url={"https://unity-sds.gitbook.io/docs"}
41-
/>
42-
);
43-
44-
appCards = appCards.sort( (a, b) => {
45-
if( a.props.title < b.props.title ) {
46-
return -1;
47-
}
48-
if( a.props.title > b.props.title ) {
49-
return 1;
50-
}
51-
return 0;
52-
});
53-
5426
return (
5527
<>
5628
<DocumentMeta

src/state/slices/healthSlice.ts

+25-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import axios, { AxiosRequestConfig } from 'axios';
22
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
33
import Config from "../../Config";
44
import { GetToken } from '../../AuthorizationWrapper';
5+
import { formatRoute } from '../../utils/routes';
56

67
enum HEALTH_ACTIONS {
78
GET_HEALTH = "health/getHealth",
@@ -18,9 +19,12 @@ export type Service = {
1819
componentCategory:string;
1920
componentType:string;
2021
description:string;
22+
healthChecks: Array<HealthCheck>;
2123
healthCheckUrl:string;
2224
landingPageUrl:string;
23-
healthChecks: Array<HealthCheck>;
25+
nativeRoute:boolean;
26+
reportHealthStatus:boolean;
27+
route:string; // needed for portal routing
2428
ssmKey:string;
2529
};
2630

@@ -81,8 +85,26 @@ const healthSlice = createSlice({
8185
state.status = "succeeded";
8286
state.lastUpdated = Date.now();
8387

84-
// Parse and store the fetched data into the state
85-
const data = action.payload;
88+
const data = Config.general.defaultRoutes;
89+
90+
// Add portal route for each application
91+
action.payload.forEach( (service:Service) => {
92+
service.nativeRoute = false;
93+
service.route = "/applications/" + formatRoute(service.componentName);
94+
service.reportHealthStatus = true;
95+
data.push(service);
96+
})
97+
98+
// sort services alphabetically by their componentName
99+
data.sort( (a:Service, b:Service) => {
100+
101+
if( a.componentName > b.componentName ) return 1;
102+
if( a.componentName < b.componentName ) return -1;
103+
104+
return 0;
105+
106+
});
107+
86108
state.items = data;
87109

88110
});

src/types/process.d.tsx

-5
This file was deleted.

src/utils/strings.ts renamed to src/utils/routes.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ export const formatRoute = (str:string) => {
33
let cleanedStr = str.toLowerCase();
44
const charReplacements = {
55
" ": "-",
6-
"_": "-"
6+
"_": "-",
7+
"(": "",
8+
")": "",
79
};
810

911
for( const [key, value] of Object.entries(charReplacements) ) {

0 commit comments

Comments
 (0)