From e6b47d85d6c70f7e57a72b465ecc3d6716cd73c9 Mon Sep 17 00:00:00 2001
From: Simon Larsen
Date: Wed, 2 Oct 2024 21:48:40 +0100
Subject: [PATCH] add breadcrumbs
---
.../Server/Utils/Monitor/MonitorResource.ts | 6 +-
Dashboard/src/Components/Alert/Alert.tsx | 37 +++
Dashboard/src/Components/Alert/AlertTable.tsx | 288 ++++++++++++++++++
.../src/Pages/Alerts/View/StateTimeline.tsx | 22 +-
Dashboard/src/Pages/Incidents/View/Index.tsx | 9 +-
.../src/Utils/Breadcrumbs/AlertBreadcrumbs.ts | 54 ++++
6 files changed, 388 insertions(+), 28 deletions(-)
create mode 100644 Dashboard/src/Components/Alert/Alert.tsx
create mode 100644 Dashboard/src/Components/Alert/AlertTable.tsx
create mode 100644 Dashboard/src/Utils/Breadcrumbs/AlertBreadcrumbs.ts
diff --git a/Common/Server/Utils/Monitor/MonitorResource.ts b/Common/Server/Utils/Monitor/MonitorResource.ts
index 69afdc4b48..f05447139f 100644
--- a/Common/Server/Utils/Monitor/MonitorResource.ts
+++ b/Common/Server/Utils/Monitor/MonitorResource.ts
@@ -42,7 +42,7 @@ import ProbeMonitorResponse from "Common/Types/Probe/ProbeMonitorResponse";
import Typeof from "Common/Types/Typeof";
import MonitorMetricsByMinute from "Common/Models/AnalyticsModels/MonitorMetricsByMinute";
import Incident, {
- TelemetryIncidentQuery,
+ TelemetryQuery,
} from "Common/Models/DatabaseModels/Incident";
import IncidentSeverity from "Common/Models/DatabaseModels/IncidentSeverity";
import IncidentStateTimeline from "Common/Models/DatabaseModels/IncidentStateTimeline";
@@ -376,7 +376,7 @@ export default class MonitorResourceUtil {
}`,
);
- let telemetryQuery: TelemetryIncidentQuery | undefined = undefined;
+ let telemetryQuery: TelemetryQuery | undefined = undefined;
if (dataToProcess && (dataToProcess as LogMonitorResponse).logQuery) {
telemetryQuery = {
@@ -751,7 +751,7 @@ export default class MonitorResourceUtil {
Array
>;
props: {
- telemetryQuery?: TelemetryIncidentQuery | undefined;
+ telemetryQuery?: TelemetryQuery | undefined;
};
}): Promise {
// criteria filters are met, now process the actions.
diff --git a/Dashboard/src/Components/Alert/Alert.tsx b/Dashboard/src/Components/Alert/Alert.tsx
new file mode 100644
index 0000000000..7cf143d9fc
--- /dev/null
+++ b/Dashboard/src/Components/Alert/Alert.tsx
@@ -0,0 +1,37 @@
+import PageMap from "../../Utils/PageMap";
+import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
+import Route from "Common/Types/API/Route";
+import ObjectID from "Common/Types/ObjectID";
+import Link from "Common/UI/Components/Link/Link";
+import Alert from "Common/Models/DatabaseModels/Alert";
+import React, { FunctionComponent, ReactElement } from "react";
+
+export interface ComponentProps {
+ alert: Alert;
+ onNavigateComplete?: (() => void) | undefined;
+}
+
+const AlertElement: FunctionComponent = (
+ props: ComponentProps,
+): ReactElement => {
+ if (props.alert._id) {
+ return (
+
+ {props.alert.title}
+
+ );
+ }
+
+ return {props.alert.title};
+};
+
+export default AlertElement;
diff --git a/Dashboard/src/Components/Alert/AlertTable.tsx b/Dashboard/src/Components/Alert/AlertTable.tsx
new file mode 100644
index 0000000000..0edb35f20c
--- /dev/null
+++ b/Dashboard/src/Components/Alert/AlertTable.tsx
@@ -0,0 +1,288 @@
+import LabelsElement from "../../Components/Label/Labels";
+import DashboardNavigation from "../../Utils/Navigation";
+import AlertElement from "./Alert";
+import { Black } from "Common/Types/BrandColors";
+import { JSONObject } from "Common/Types/JSON";
+import FormValues from "Common/UI/Components/Forms/Types/FormValues";
+import ConfirmModal from "Common/UI/Components/Modal/ConfirmModal";
+import { ModalTableBulkDefaultActions } from "Common/UI/Components/ModelTable/BaseModelTable";
+import ModelTable from "Common/UI/Components/ModelTable/ModelTable";
+import Pill from "Common/UI/Components/Pill/Pill";
+import FieldType from "Common/UI/Components/Types/FieldType";
+import Query from "Common/Types/BaseDatabase/Query";
+import Alert from "Common/Models/DatabaseModels/Alert";
+import AlertSeverity from "Common/Models/DatabaseModels/AlertSeverity";
+import AlertState from "Common/Models/DatabaseModels/AlertState";
+import Label from "Common/Models/DatabaseModels/Label";
+import Monitor from "Common/Models/DatabaseModels/Monitor";
+import React, { FunctionComponent, ReactElement, useState } from "react";
+import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
+import PageMap from "../../Utils/PageMap";
+import MonitorElement from "../Monitor/Monitor";
+
+export interface ComponentProps {
+ query?: Query | undefined;
+ noItemsMessage?: string | undefined;
+ title?: string | undefined;
+ description?: string | undefined;
+ createInitialValues?: FormValues | undefined;
+ disableCreate?: boolean | undefined;
+}
+
+const AlertsTable: FunctionComponent = (
+ props: ComponentProps,
+): ReactElement => {
+
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState("");
+ const [showAlertTemplateModal, setShowAlertTemplateModal] =
+ useState(false);
+ const [initialValuesForAlert, setInitialValuesForAlert] =
+ useState({});
+
+ return (
+ <>
+
+ name="Alerts"
+ bulkActions={{
+ buttons: [ModalTableBulkDefaultActions.Delete],
+ }}
+ onCreateEditModalClose={(): void => {
+ setInitialValuesForAlert({});
+ }}
+ modelType={Alert}
+ id="alerts-table"
+ isDeleteable={false}
+ showCreateForm={Object.keys(initialValuesForAlert).length > 0}
+ query={props.query || {}}
+ isEditable={false}
+ isCreateable={!props.disableCreate}
+ isViewable={true}
+ createInitialValues={
+ Object.keys(initialValuesForAlert).length > 0
+ ? initialValuesForAlert
+ : props.createInitialValues
+ }
+ cardProps={{
+ title: props.title || "Alerts",
+ description:
+ props.description ||
+ "Here is a list of alerts for this project.",
+ }}
+ noItemsMessage={props.noItemsMessage || "No alerts found."}
+ showRefreshButton={true}
+ showViewIdButton={true}
+ viewPageRoute={RouteUtil.populateRouteParams(
+ RouteMap[PageMap.INCIDENTS]!,
+ )}
+ filters={[
+ {
+ title: "Alert ID",
+ type: FieldType.Text,
+ field: {
+ _id: true,
+ },
+ },
+ {
+ field: {
+ title: true,
+ },
+ title: "Title",
+ type: FieldType.Text,
+ },
+ {
+ field: {
+ alertSeverity: {
+ name: true,
+ },
+ },
+ title: "Severity",
+ type: FieldType.Entity,
+
+ filterEntityType: AlertSeverity,
+ filterQuery: {
+ projectId: DashboardNavigation.getProjectId()!,
+ },
+ filterDropdownField: {
+ label: "name",
+ value: "_id",
+ },
+ },
+ {
+ field: {
+ currentAlertState: {
+ name: true,
+ color: true,
+ },
+ },
+ title: "State",
+ type: FieldType.Entity,
+
+ filterEntityType: AlertState,
+ filterQuery: {
+ projectId: DashboardNavigation.getProjectId()!,
+ },
+ filterDropdownField: {
+ label: "name",
+ value: "_id",
+ },
+ },
+ {
+ field: {
+ monitor: {
+ name: true,
+ _id: true,
+ projectId: true,
+ },
+ },
+ title: "Monitor Affected",
+ type: FieldType.EntityArray,
+
+ filterEntityType: Monitor,
+ filterQuery: {
+ projectId: DashboardNavigation.getProjectId()!,
+ },
+ filterDropdownField: {
+ label: "name",
+ value: "_id",
+ },
+ },
+ {
+ field: {
+ createdAt: true,
+ },
+ title: "Created",
+ type: FieldType.Date,
+ },
+ {
+ field: {
+ labels: {
+ name: true,
+ },
+ },
+ title: "Labels",
+ type: FieldType.EntityArray,
+
+ filterEntityType: Label,
+ filterQuery: {
+ projectId: DashboardNavigation.getProjectId()!,
+ },
+ filterDropdownField: {
+ label: "name",
+ value: "_id",
+ },
+ },
+ ]}
+ columns={[
+ {
+ field: {
+ title: true,
+ },
+ title: "Title",
+ type: FieldType.Element,
+ getElement: (item: Alert): ReactElement => {
+ return ;
+ },
+ },
+ {
+ field: {
+ currentAlertState: {
+ name: true,
+ color: true,
+ },
+ },
+ title: "State",
+ type: FieldType.Entity,
+
+ getElement: (item: Alert): ReactElement => {
+ if (item["currentAlertState"]) {
+ return (
+
+ );
+ }
+
+ return <>>;
+ },
+ },
+ {
+ field: {
+ alertSeverity: {
+ name: true,
+ color: true,
+ },
+ },
+
+ title: "Severity",
+ type: FieldType.Entity,
+ getElement: (item: Alert): ReactElement => {
+ if (item["alertSeverity"]) {
+ return (
+
+ );
+ }
+
+ return <>>;
+ },
+ },
+ {
+ field: {
+ monitor: {
+ name: true,
+ _id: true,
+ projectId: true,
+ },
+ },
+ title: "Monitors Affected",
+ type: FieldType.EntityArray,
+
+ getElement: (item: Alert): ReactElement => {
+ return ;
+ },
+ },
+ {
+ field: {
+ createdAt: true,
+ },
+ title: "Created",
+ type: FieldType.DateTime,
+ },
+ {
+ field: {
+ labels: {
+ name: true,
+ color: true,
+ },
+ },
+ title: "Labels",
+ type: FieldType.EntityArray,
+
+ getElement: (item: Alert): ReactElement => {
+ return ;
+ },
+ },
+ ]}
+ />
+
+ {error && (
+ {
+ return setError("");
+ }}
+ />
+ )}
+ >
+ );
+};
+
+export default AlertsTable;
diff --git a/Dashboard/src/Pages/Alerts/View/StateTimeline.tsx b/Dashboard/src/Pages/Alerts/View/StateTimeline.tsx
index 2ddbe9b26f..66479021ad 100644
--- a/Dashboard/src/Pages/Alerts/View/StateTimeline.tsx
+++ b/Dashboard/src/Pages/Alerts/View/StateTimeline.tsx
@@ -120,18 +120,7 @@ const AlertViewStateTimeline: FunctionComponent = (
labelField: "name",
valueField: "_id",
},
- },
- {
- field: {
- shouldStatusPageSubscribersBeNotified: true,
- },
-
- title: "Notify Status Page Subscribers",
- description: "Should status page subscribers be notified?",
- fieldType: FormFieldSchemaType.Checkbox,
- defaultValue: true,
- required: false,
- },
+ }
]}
showRefreshButton={true}
viewPageRoute={Navigation.getCurrentRoute()}
@@ -223,14 +212,7 @@ const AlertViewStateTimeline: FunctionComponent = (
);
},
- },
- {
- field: {
- shouldStatusPageSubscribersBeNotified: true,
- },
- title: "Subscribers Notified",
- type: FieldType.Boolean,
- },
+ }
]}
/>
{showViewLogsModal ? (
diff --git a/Dashboard/src/Pages/Incidents/View/Index.tsx b/Dashboard/src/Pages/Incidents/View/Index.tsx
index 61e894d4d3..5546d526ab 100644
--- a/Dashboard/src/Pages/Incidents/View/Index.tsx
+++ b/Dashboard/src/Pages/Incidents/View/Index.tsx
@@ -28,9 +28,7 @@ import BaseAPI from "Common/UI/Utils/API/API";
import GlobalEvent from "Common/UI/Utils/GlobalEvents";
import ModelAPI, { ListResult } from "Common/UI/Utils/ModelAPI/ModelAPI";
import Navigation from "Common/UI/Utils/Navigation";
-import Incident, {
- TelemetryIncidentQuery,
-} from "Common/Models/DatabaseModels/Incident";
+import Incident from "Common/Models/DatabaseModels/Incident";
import IncidentSeverity from "Common/Models/DatabaseModels/IncidentSeverity";
import IncidentState from "Common/Models/DatabaseModels/IncidentState";
import IncidentStateTimeline from "Common/Models/DatabaseModels/IncidentStateTimeline";
@@ -48,6 +46,7 @@ import DashboardLogsViewer from "../../../Components/Logs/LogsViewer";
import TelemetryType from "Common/Types/Telemetry/TelemetryType";
import JSONFunctions from "Common/Types/JSONFunctions";
import TraceTable from "../../../Components/Traces/TraceTable";
+import { TelemetryQuery } from "Common/Types/Telemetry/TelemetryQuery";
const IncidentView: FunctionComponent<
PageComponentProps
@@ -63,7 +62,7 @@ const IncidentView: FunctionComponent<
const [isLoading, setIsLoading] = useState(false);
const [telemetryQuery, setTelemetryQuery] =
- useState(null);
+ useState(null);
const fetchData: PromiseVoidFunction = async (): Promise => {
try {
@@ -114,7 +113,7 @@ const IncidentView: FunctionComponent<
},
});
- let telemetryQuery: TelemetryIncidentQuery | null = null;
+ let telemetryQuery: TelemetryQuery | null = null;
if (incident?.telemetryQuery) {
telemetryQuery = JSONFunctions.deserialize(
diff --git a/Dashboard/src/Utils/Breadcrumbs/AlertBreadcrumbs.ts b/Dashboard/src/Utils/Breadcrumbs/AlertBreadcrumbs.ts
new file mode 100644
index 0000000000..bfe57c67ae
--- /dev/null
+++ b/Dashboard/src/Utils/Breadcrumbs/AlertBreadcrumbs.ts
@@ -0,0 +1,54 @@
+import PageMap from "../PageMap";
+import { BuildBreadcrumbLinksByTitles } from "./Helper";
+import Dictionary from "Common/Types/Dictionary";
+import Link from "Common/Types/Link";
+
+export function getAlertsBreadcrumbs(path: string): Array | undefined {
+ const breadcrumpLinksMap: Dictionary = {
+ ...BuildBreadcrumbLinksByTitles(PageMap.ALERTS, [
+ "Project",
+ "Alerts",
+ ]),
+ ...BuildBreadcrumbLinksByTitles(PageMap.UNRESOLVED_ALERTS, [
+ "Project",
+ "Alerts",
+ "Active Alerts",
+ ]),
+ ...BuildBreadcrumbLinksByTitles(PageMap.ALERT_VIEW, [
+ "Project",
+ "Alerts",
+ "View Alert",
+ ]),
+ ...BuildBreadcrumbLinksByTitles(PageMap.ALERT_VIEW_STATE_TIMELINE, [
+ "Project",
+ "Alerts",
+ "View Alert",
+ "State Timeline",
+ ]),
+ ...BuildBreadcrumbLinksByTitles(PageMap.ALERT_VIEW_OWNERS, [
+ "Project",
+ "Alerts",
+ "View Alert",
+ "Owners",
+ ]),
+ ...BuildBreadcrumbLinksByTitles(PageMap.ALERT_INTERNAL_NOTE, [
+ "Project",
+ "Alerts",
+ "View Alert",
+ "Private Notes",
+ ]),
+ ...BuildBreadcrumbLinksByTitles(PageMap.ALERT_VIEW_CUSTOM_FIELDS, [
+ "Project",
+ "Alerts",
+ "View Alert",
+ "Custom Fields",
+ ]),
+ ...BuildBreadcrumbLinksByTitles(PageMap.ALERT_VIEW_DELETE, [
+ "Project",
+ "Alerts",
+ "View Alert",
+ "Delete Alert",
+ ]),
+ };
+ return breadcrumpLinksMap[path];
+}