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]; +}