add active alerts to home page

This commit is contained in:
Simon Larsen 2024-10-04 08:52:42 -07:00
parent 6acb0fb486
commit 4be7675b74
No known key found for this signature in database
GPG Key ID: 96C5DCA24769DBCA
6 changed files with 198 additions and 0 deletions

View File

@ -10,6 +10,7 @@ import UserProfilePicture from "./Pages/Global/UserProfile/Picture";
// Pages
import Home from "./Pages/Home/Home";
import NotOperationalMonitors from "./Pages/Home/NotOperationalMonitors";
import HomeActiveAlerts from "./Pages/Home/ActiveAlerts";
import OngoingScheduledEvents from "./Pages/Home/OngoingScheduledMaintenance";
import Logout from "./Pages/Logout/Logout";
import Sso from "./Pages/Onboarding/SSO";
@ -265,6 +266,16 @@ const App: () => JSX.Element = () => {
}
/>
<PageRoute
path={RouteMap[PageMap.HOME_ACTIVE_ALERTS]?.toString() || ""}
element={
<HomeActiveAlerts
{...commonPageProps}
pageRoute={RouteMap[PageMap.HOME_ACTIVE_ALERTS] as Route}
/>
}
/>
<PageRoute
path={
RouteMap[

View File

@ -0,0 +1,96 @@
import AlertsTable from "../../Components/Alert/AlertsTable";
import AlertStateUtil from "../../Utils/AlertState";
import DashboardNavigation from "../../Utils/Navigation";
import PageMap from "../../Utils/PageMap";
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
import PageComponentProps from "../PageComponentProps";
import DashboardSideMenu from "./SideMenu";
import Route from "Common/Types/API/Route";
import Includes from "Common/Types/BaseDatabase/Includes";
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import PageLoader from "Common/UI/Components/Loader/PageLoader";
import Page from "Common/UI/Components/Page/Page";
import API from "Common/UI/Utils/API/API";
import AlertState from "Common/Models/DatabaseModels/AlertState";
import React, {
FunctionComponent,
ReactElement,
useEffect,
useState,
} from "react";
const Home: FunctionComponent<PageComponentProps> = (
props: PageComponentProps,
): ReactElement => {
const [unresolvedAlertStates, setUnresolvedAlertStates] = useState<
Array<AlertState>
>([]);
const [error, setError] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(false);
const fetchAlertStates: PromiseVoidFunction = async (): Promise<void> => {
setIsLoading(true);
try {
setUnresolvedAlertStates(
await AlertStateUtil.getUnresolvedAlertStates(
DashboardNavigation.getProjectId()!,
),
);
setError("");
} catch (err) {
setError(API.getFriendlyMessage(err));
}
setIsLoading(false);
};
useEffect(() => {
fetchAlertStates().catch((err: Error) => {
setError(API.getFriendlyMessage(err));
});
}, []);
return (
<Page
title={"Home"}
breadcrumbLinks={[
{
title: "Project",
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
},
{
title: "Home",
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
},
]}
sideMenu={
<DashboardSideMenu project={props.currentProject || undefined} />
}
>
<div>
{isLoading && <PageLoader isVisible={true} />}
{error && <ErrorMessage error={error} />}
{!isLoading && !error && unresolvedAlertStates.length > 0 && (
<AlertsTable
query={{
projectId: DashboardNavigation.getProjectId()!,
currentAlertStateId: new Includes(
unresolvedAlertStates.map((state: AlertState) => {
return state.id!;
}),
),
}}
noItemsMessage="Nice work! No Active Alerts so far."
title="Active Alerts"
description="Here is a list of all the Active Alerts for this project."
/>
)}
</div>
</Page>
);
};
export default Home;

View File

@ -20,6 +20,9 @@ import React, {
useEffect,
useState,
} from "react";
import AlertState from "Common/Models/DatabaseModels/AlertState";
import Alert from "Common/Models/DatabaseModels/Alert";
import AlertStateUtil from "../../Utils/AlertState";
export interface ComponentProps {
project?: Project | undefined;
@ -32,6 +35,10 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
Array<IncidentState>
>([]);
const [unresolvedAlertStates, setUnresolvedAlertStates] = useState<
Array<AlertState>
>([]);
const fetchIncidentStates: PromiseVoidFunction = async (): Promise<void> => {
try {
if (props.project?.id) {
@ -46,10 +53,26 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
}
};
const fetchAlertStates: PromiseVoidFunction = async (): Promise<void> => {
try {
if (props.project?.id) {
const unresolvedAlertStates: Array<AlertState> =
await AlertStateUtil.getUnresolvedAlertStates(props.project?.id);
setUnresolvedAlertStates(unresolvedAlertStates);
}
} catch (err) {
// maybe show an error message
}
};
useEffect(() => {
fetchIncidentStates().catch((_err: Error) => {
// do nothing
});
fetchAlertStates().catch((_err: Error) => {
// do nothing
});
}, []);
return (
@ -74,6 +97,28 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
/>
</SideMenuSection>
<SideMenuSection title="Alerts">
<SideMenuItem<Alert>
link={{
title: "Active",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.HOME_ACTIVE_ALERTS] as Route,
),
}}
icon={IconProp.ExclaimationCircle}
badgeType={BadgeType.DANGER}
modelType={Alert}
countQuery={{
projectId: props.project?._id,
currentAlertStateId: new Includes(
unresolvedAlertStates.map((state: AlertState) => {
return state.id!;
}),
),
}}
/>
</SideMenuSection>
<SideMenuSection title="Monitors">
<SideMenuItem<Monitor>
link={{

View File

@ -0,0 +1,41 @@
import SortOrder from "Common/Types/BaseDatabase/SortOrder";
import { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
import ObjectID from "Common/Types/ObjectID";
import ListResult from "Common/UI/Utils/BaseDatabase/ListResult";
import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
import AlertState from "Common/Models/DatabaseModels/AlertState";
export default class AlertStateUtil {
public static async getUnresolvedAlertStates(
projectId: ObjectID,
): Promise<AlertState[]> {
const alertStates: ListResult<AlertState> =
await ModelAPI.getList<AlertState>({
modelType: AlertState,
query: {
projectId: projectId,
},
skip: 0,
limit: LIMIT_PER_PROJECT,
sort: {
order: SortOrder.Ascending,
},
select: {
_id: true,
isResolvedState: true,
},
});
const unresolvedAlertStates: Array<AlertState> = [];
for (const state of alertStates.data) {
if (!state.isResolvedState) {
unresolvedAlertStates.push(state);
} else {
break; // everything after resolved state is resolved
}
}
return unresolvedAlertStates;
}
}

View File

@ -74,6 +74,7 @@ enum PageMap {
HOME = "HOME",
HOME_NOT_OPERATIONAL_MONITORS = "HOME_NOT_OPERATIONAL_MONITORS",
HOME_ONGOING_SCHEDULED_MAINTENANCE_EVENTS = "HOME_ONGOING_SCHEDULED_MAINTENANCE_EVENTS",
HOME_ACTIVE_ALERTS = "HOME_ACTIVE_ALERTS",
INCIDENTS_ROOT = "INCIDENTS_ROOT",
INCIDENTS = "INCIDENTS",

View File

@ -267,6 +267,10 @@ const RouteMap: Dictionary<Route> = {
`/dashboard/${RouteParams.ProjectID}/home/monitors-inoperational`,
),
[PageMap.HOME_ACTIVE_ALERTS]: new Route(
`/dashboard/${RouteParams.ProjectID}/home/active-alerts`,
),
[PageMap.HOME_ONGOING_SCHEDULED_MAINTENANCE_EVENTS]: new Route(
`/dashboard/${RouteParams.ProjectID}/home/scheduled-maintenance-ongoing`,
),