mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: Add Exceptions documentation page and update routing, breadcrumbs, and side menu
This commit is contained in:
@@ -1,20 +1,7 @@
|
||||
import PageComponentProps from "../PageComponentProps";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import React, {
|
||||
Fragment,
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useCallback,
|
||||
useState,
|
||||
} from "react";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import ExceptionsTable from "../../Components/Exceptions/ExceptionsTable";
|
||||
import TelemetryDocumentation from "../../Components/Telemetry/Documentation";
|
||||
import Button, {
|
||||
ButtonSize,
|
||||
ButtonStyleType,
|
||||
} from "Common/UI/Components/Button/Button";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import TelemetryException from "Common/Models/DatabaseModels/TelemetryException";
|
||||
|
||||
const ArchivedExceptionsPage: FunctionComponent<PageComponentProps> = (
|
||||
props: PageComponentProps,
|
||||
@@ -22,19 +9,6 @@ const ArchivedExceptionsPage: FunctionComponent<PageComponentProps> = (
|
||||
const disableTelemetryForThisProject: boolean =
|
||||
props.currentProject?.reseller?.enableTelemetryFeatures === false;
|
||||
|
||||
const [hasData, setHasData] = useState<boolean>(false);
|
||||
const [showDocs, setShowDocs] = useState<boolean>(false);
|
||||
|
||||
const handleFetchSuccess: (
|
||||
data: Array<TelemetryException>,
|
||||
totalCount: number,
|
||||
) => void = useCallback(
|
||||
(_data: Array<TelemetryException>, totalCount: number) => {
|
||||
setHasData(totalCount > 0);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
if (disableTelemetryForThisProject) {
|
||||
return (
|
||||
<ErrorMessage message="Looks like you have bought this plan from a reseller. It did not include telemetry features in your plan. Telemetry features are disabled for this project." />
|
||||
@@ -42,33 +16,13 @@ const ArchivedExceptionsPage: FunctionComponent<PageComponentProps> = (
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<ExceptionsTable
|
||||
query={{
|
||||
isArchived: true,
|
||||
}}
|
||||
title="Archived Exceptions"
|
||||
description="All the exceptions that have been archived. You will not be notified about these exceptions."
|
||||
onFetchSuccess={handleFetchSuccess}
|
||||
/>
|
||||
{!hasData && <TelemetryDocumentation telemetryType="exceptions" />}
|
||||
{hasData && !showDocs && (
|
||||
<div className="flex justify-center mt-4 mb-4">
|
||||
<Button
|
||||
title="View Setup Documentation"
|
||||
icon={IconProp.Book}
|
||||
buttonSize={ButtonSize.Small}
|
||||
buttonStyle={ButtonStyleType.OUTLINE}
|
||||
onClick={() => {
|
||||
setShowDocs(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{hasData && showDocs && (
|
||||
<TelemetryDocumentation telemetryType="exceptions" />
|
||||
)}
|
||||
</Fragment>
|
||||
<ExceptionsTable
|
||||
query={{
|
||||
isArchived: true,
|
||||
}}
|
||||
title="Archived Exceptions"
|
||||
description="All the exceptions that have been archived. You will not be notified about these exceptions."
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import PageComponentProps from "../PageComponentProps";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import TelemetryDocumentation from "../../Components/Telemetry/Documentation";
|
||||
|
||||
const ExceptionsDocumentationPage: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps,
|
||||
): ReactElement => {
|
||||
return <TelemetryDocumentation telemetryType="exceptions" />;
|
||||
};
|
||||
|
||||
export default ExceptionsDocumentationPage;
|
||||
@@ -1,20 +1,7 @@
|
||||
import PageComponentProps from "../PageComponentProps";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import React, {
|
||||
Fragment,
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useCallback,
|
||||
useState,
|
||||
} from "react";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import ExceptionsTable from "../../Components/Exceptions/ExceptionsTable";
|
||||
import TelemetryDocumentation from "../../Components/Telemetry/Documentation";
|
||||
import Button, {
|
||||
ButtonSize,
|
||||
ButtonStyleType,
|
||||
} from "Common/UI/Components/Button/Button";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import TelemetryException from "Common/Models/DatabaseModels/TelemetryException";
|
||||
|
||||
const ResolvedExceptionsPage: FunctionComponent<PageComponentProps> = (
|
||||
props: PageComponentProps,
|
||||
@@ -22,19 +9,6 @@ const ResolvedExceptionsPage: FunctionComponent<PageComponentProps> = (
|
||||
const disableTelemetryForThisProject: boolean =
|
||||
props.currentProject?.reseller?.enableTelemetryFeatures === false;
|
||||
|
||||
const [hasData, setHasData] = useState<boolean>(false);
|
||||
const [showDocs, setShowDocs] = useState<boolean>(false);
|
||||
|
||||
const handleFetchSuccess: (
|
||||
data: Array<TelemetryException>,
|
||||
totalCount: number,
|
||||
) => void = useCallback(
|
||||
(_data: Array<TelemetryException>, totalCount: number) => {
|
||||
setHasData(totalCount > 0);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
if (disableTelemetryForThisProject) {
|
||||
return (
|
||||
<ErrorMessage message="Looks like you have bought this plan from a reseller. It did not include telemetry features in your plan. Telemetry features are disabled for this project." />
|
||||
@@ -42,34 +16,14 @@ const ResolvedExceptionsPage: FunctionComponent<PageComponentProps> = (
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<ExceptionsTable
|
||||
query={{
|
||||
isResolved: true,
|
||||
isArchived: false,
|
||||
}}
|
||||
title="Resolved Exceptions"
|
||||
description="All the exceptions that have been resolved."
|
||||
onFetchSuccess={handleFetchSuccess}
|
||||
/>
|
||||
{!hasData && <TelemetryDocumentation telemetryType="exceptions" />}
|
||||
{hasData && !showDocs && (
|
||||
<div className="flex justify-center mt-4 mb-4">
|
||||
<Button
|
||||
title="View Setup Documentation"
|
||||
icon={IconProp.Book}
|
||||
buttonSize={ButtonSize.Small}
|
||||
buttonStyle={ButtonStyleType.OUTLINE}
|
||||
onClick={() => {
|
||||
setShowDocs(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{hasData && showDocs && (
|
||||
<TelemetryDocumentation telemetryType="exceptions" />
|
||||
)}
|
||||
</Fragment>
|
||||
<ExceptionsTable
|
||||
query={{
|
||||
isResolved: true,
|
||||
isArchived: false,
|
||||
}}
|
||||
title="Resolved Exceptions"
|
||||
description="All the exceptions that have been resolved."
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -49,6 +49,15 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
icon: IconProp.Archive,
|
||||
},
|
||||
{
|
||||
link: {
|
||||
title: "Documentation",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.EXCEPTIONS_DOCUMENTATION] as Route,
|
||||
),
|
||||
},
|
||||
icon: IconProp.Book,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
import PageComponentProps from "../PageComponentProps";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import React, {
|
||||
Fragment,
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useCallback,
|
||||
useState,
|
||||
} from "react";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import ExceptionsTable from "../../Components/Exceptions/ExceptionsTable";
|
||||
import TelemetryDocumentation from "../../Components/Telemetry/Documentation";
|
||||
import Button, {
|
||||
ButtonSize,
|
||||
ButtonStyleType,
|
||||
} from "Common/UI/Components/Button/Button";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import TelemetryException from "Common/Models/DatabaseModels/TelemetryException";
|
||||
|
||||
const UnresolvedExceptionsPage: FunctionComponent<PageComponentProps> = (
|
||||
props: PageComponentProps,
|
||||
@@ -22,19 +9,6 @@ const UnresolvedExceptionsPage: FunctionComponent<PageComponentProps> = (
|
||||
const disableTelemetryForThisProject: boolean =
|
||||
props.currentProject?.reseller?.enableTelemetryFeatures === false;
|
||||
|
||||
const [hasData, setHasData] = useState<boolean>(false);
|
||||
const [showDocs, setShowDocs] = useState<boolean>(false);
|
||||
|
||||
const handleFetchSuccess: (
|
||||
data: Array<TelemetryException>,
|
||||
totalCount: number,
|
||||
) => void = useCallback(
|
||||
(_data: Array<TelemetryException>, totalCount: number) => {
|
||||
setHasData(totalCount > 0);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
if (disableTelemetryForThisProject) {
|
||||
return (
|
||||
<ErrorMessage message="Looks like you have bought this plan from a reseller. It did not include telemetry features in your plan. Telemetry features are disabled for this project." />
|
||||
@@ -42,34 +16,14 @@ const UnresolvedExceptionsPage: FunctionComponent<PageComponentProps> = (
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<ExceptionsTable
|
||||
query={{
|
||||
isResolved: false,
|
||||
isArchived: false,
|
||||
}}
|
||||
title="Unresolved Exceptions"
|
||||
description="All the exceptions that have not been resolved."
|
||||
onFetchSuccess={handleFetchSuccess}
|
||||
/>
|
||||
{!hasData && <TelemetryDocumentation telemetryType="exceptions" />}
|
||||
{hasData && !showDocs && (
|
||||
<div className="flex justify-center mt-4 mb-4">
|
||||
<Button
|
||||
title="View Setup Documentation"
|
||||
icon={IconProp.Book}
|
||||
buttonSize={ButtonSize.Small}
|
||||
buttonStyle={ButtonStyleType.OUTLINE}
|
||||
onClick={() => {
|
||||
setShowDocs(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{hasData && showDocs && (
|
||||
<TelemetryDocumentation telemetryType="exceptions" />
|
||||
)}
|
||||
</Fragment>
|
||||
<ExceptionsTable
|
||||
query={{
|
||||
isResolved: false,
|
||||
isArchived: false,
|
||||
}}
|
||||
title="Unresolved Exceptions"
|
||||
description="All the exceptions that have not been resolved."
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import InBetween from "Common/Types/BaseDatabase/InBetween";
|
||||
|
||||
interface KubernetesEvent {
|
||||
timestamp: string;
|
||||
@@ -69,10 +70,7 @@ const KubernetesClusterEvents: FunctionComponent<
|
||||
modelType: Log,
|
||||
query: {
|
||||
projectId: ProjectUtil.getCurrentProjectId()!.toString(),
|
||||
time: {
|
||||
startValue: startDate,
|
||||
endValue: endDate,
|
||||
} as any,
|
||||
time: new InBetween<Date>(startDate, endDate),
|
||||
},
|
||||
limit: 200,
|
||||
skip: 0,
|
||||
@@ -88,6 +86,63 @@ const KubernetesClusterEvents: FunctionComponent<
|
||||
requestOptions: {},
|
||||
});
|
||||
|
||||
// Helper to extract a string value from OTLP kvlistValue
|
||||
const getKvValue = (
|
||||
kvList: JSONObject | undefined,
|
||||
key: string,
|
||||
): string => {
|
||||
if (!kvList) {
|
||||
return "";
|
||||
}
|
||||
const values = (kvList as JSONObject)["values"] as Array<JSONObject> | undefined;
|
||||
if (!values) {
|
||||
return "";
|
||||
}
|
||||
for (const entry of values) {
|
||||
if (entry["key"] === key) {
|
||||
const val = entry["value"] as JSONObject | undefined;
|
||||
if (!val) {
|
||||
return "";
|
||||
}
|
||||
if (val["stringValue"]) {
|
||||
return val["stringValue"] as string;
|
||||
}
|
||||
if (val["intValue"]) {
|
||||
return String(val["intValue"]);
|
||||
}
|
||||
// Nested kvlist (e.g., regarding, metadata)
|
||||
if (val["kvlistValue"]) {
|
||||
return val["kvlistValue"] as unknown as string;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
// Helper to get nested kvlist value
|
||||
const getNestedKvValue = (
|
||||
kvList: JSONObject | undefined,
|
||||
parentKey: string,
|
||||
childKey: string,
|
||||
): string => {
|
||||
if (!kvList) {
|
||||
return "";
|
||||
}
|
||||
const values = (kvList as JSONObject)["values"] as Array<JSONObject> | undefined;
|
||||
if (!values) {
|
||||
return "";
|
||||
}
|
||||
for (const entry of values) {
|
||||
if (entry["key"] === parentKey) {
|
||||
const val = entry["value"] as JSONObject | undefined;
|
||||
if (val && val["kvlistValue"]) {
|
||||
return getKvValue(val["kvlistValue"] as JSONObject, childKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
const k8sEvents: Array<KubernetesEvent> = [];
|
||||
|
||||
for (const log of listResult.data) {
|
||||
@@ -95,35 +150,55 @@ const KubernetesClusterEvents: FunctionComponent<
|
||||
|
||||
// Filter to only k8s events from this cluster
|
||||
if (
|
||||
attrs["k8s.cluster.name"] !== item.clusterIdentifier &&
|
||||
attrs["k8s_cluster_name"] !== item.clusterIdentifier
|
||||
attrs["resource.k8s.cluster.name"] !== item.clusterIdentifier &&
|
||||
attrs["k8s.cluster.name"] !== item.clusterIdentifier
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// k8sobjects receiver events have k8s event attributes
|
||||
const eventType: string =
|
||||
(attrs["k8s.event.type"] as string) ||
|
||||
(attrs["type"] as string) ||
|
||||
"";
|
||||
const reason: string =
|
||||
(attrs["k8s.event.reason"] as string) ||
|
||||
(attrs["reason"] as string) ||
|
||||
"";
|
||||
const objectKind: string =
|
||||
(attrs["k8s.object.kind"] as string) ||
|
||||
(attrs["involvedObject.kind"] as string) ||
|
||||
"";
|
||||
const objectName: string =
|
||||
(attrs["k8s.object.name"] as string) ||
|
||||
(attrs["involvedObject.name"] as string) ||
|
||||
"";
|
||||
const namespace: string =
|
||||
(attrs["k8s.namespace.name"] as string) ||
|
||||
(attrs["namespace"] as string) ||
|
||||
"";
|
||||
// Only process k8s event logs (from k8sobjects receiver)
|
||||
if (attrs["logAttributes.event.domain"] !== "k8s") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (eventType || reason || objectKind) {
|
||||
// Parse the body which is OTLP kvlistValue JSON
|
||||
let bodyObj: JSONObject | null = null;
|
||||
try {
|
||||
if (typeof log.body === "string") {
|
||||
bodyObj = JSON.parse(log.body) as JSONObject;
|
||||
}
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bodyObj) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The body has a top-level kvlistValue with "type" (ADDED/MODIFIED) and "object" keys
|
||||
const topKvList = bodyObj["kvlistValue"] as JSONObject | undefined;
|
||||
if (!topKvList) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the "object" which is the actual k8s Event
|
||||
const objectKvListRaw = getKvValue(topKvList, "object");
|
||||
if (!objectKvListRaw || typeof objectKvListRaw === "string") {
|
||||
continue;
|
||||
}
|
||||
const objectKvList = objectKvListRaw as unknown as JSONObject;
|
||||
|
||||
const eventType: string = getKvValue(objectKvList, "type") || "";
|
||||
const reason: string = getKvValue(objectKvList, "reason") || "";
|
||||
const note: string = getKvValue(objectKvList, "note") || "";
|
||||
|
||||
// Get object details from "regarding" sub-object
|
||||
const objectKind: string = getNestedKvValue(objectKvList, "regarding", "kind") || "";
|
||||
const objectName: string = getNestedKvValue(objectKvList, "regarding", "name") || "";
|
||||
const namespace: string = getNestedKvValue(objectKvList, "regarding", "namespace") ||
|
||||
getNestedKvValue(objectKvList, "metadata", "namespace") || "";
|
||||
|
||||
if (eventType || reason) {
|
||||
k8sEvents.push({
|
||||
timestamp: log.time
|
||||
? OneUptimeDate.getDateAsLocalFormattedString(log.time)
|
||||
@@ -133,7 +208,7 @@ const KubernetesClusterEvents: FunctionComponent<
|
||||
objectKind: objectKind || "Unknown",
|
||||
objectName: objectName || "Unknown",
|
||||
namespace: namespace || "default",
|
||||
message: log.body || "",
|
||||
message: note || "",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ const KubernetesClusterNodes: FunctionComponent<
|
||||
const attributes: Record<string, unknown> =
|
||||
(data["attributes"] as Record<string, unknown>) || {};
|
||||
const nodeName: string =
|
||||
(attributes["k8s.node.name"] as string) || "Unknown Node";
|
||||
(attributes["resource.k8s.node.name"] as string) || "Unknown Node";
|
||||
return { title: nodeName };
|
||||
};
|
||||
|
||||
@@ -88,7 +88,7 @@ const KubernetesClusterNodes: FunctionComponent<
|
||||
filterData: {
|
||||
metricName: "k8s.node.cpu.utilization",
|
||||
attributes: {
|
||||
"k8s.cluster.name": clusterIdentifier,
|
||||
"resource.k8s.cluster.name": clusterIdentifier,
|
||||
},
|
||||
aggegationType: AggregationType.Avg,
|
||||
aggregateBy: {},
|
||||
@@ -112,7 +112,7 @@ const KubernetesClusterNodes: FunctionComponent<
|
||||
filterData: {
|
||||
metricName: "k8s.node.memory.usage",
|
||||
attributes: {
|
||||
"k8s.cluster.name": clusterIdentifier,
|
||||
"resource.k8s.cluster.name": clusterIdentifier,
|
||||
},
|
||||
aggegationType: AggregationType.Avg,
|
||||
aggregateBy: {},
|
||||
@@ -136,7 +136,7 @@ const KubernetesClusterNodes: FunctionComponent<
|
||||
filterData: {
|
||||
metricName: "k8s.node.filesystem.usage",
|
||||
attributes: {
|
||||
"k8s.cluster.name": clusterIdentifier,
|
||||
"resource.k8s.cluster.name": clusterIdentifier,
|
||||
},
|
||||
aggegationType: AggregationType.Avg,
|
||||
aggregateBy: {},
|
||||
@@ -158,9 +158,10 @@ const KubernetesClusterNodes: FunctionComponent<
|
||||
},
|
||||
metricQueryData: {
|
||||
filterData: {
|
||||
metricName: "k8s.node.network.io.receive",
|
||||
metricName: "k8s.node.network.io",
|
||||
attributes: {
|
||||
"k8s.cluster.name": clusterIdentifier,
|
||||
"resource.k8s.cluster.name": clusterIdentifier,
|
||||
"metricAttributes.direction": "receive",
|
||||
},
|
||||
aggegationType: AggregationType.Avg,
|
||||
aggregateBy: {},
|
||||
|
||||
@@ -72,9 +72,9 @@ const KubernetesClusterPods: FunctionComponent<
|
||||
const attributes: Record<string, unknown> =
|
||||
(data["attributes"] as Record<string, unknown>) || {};
|
||||
const podName: string =
|
||||
(attributes["k8s.pod.name"] as string) || "Unknown Pod";
|
||||
(attributes["resource.k8s.pod.name"] as string) || "Unknown Pod";
|
||||
const namespace: string =
|
||||
(attributes["k8s.namespace.name"] as string) || "";
|
||||
(attributes["resource.k8s.namespace.name"] as string) || "";
|
||||
return { title: namespace ? `${namespace}/${podName}` : podName };
|
||||
};
|
||||
|
||||
@@ -90,7 +90,7 @@ const KubernetesClusterPods: FunctionComponent<
|
||||
filterData: {
|
||||
metricName: "k8s.pod.cpu.utilization",
|
||||
attributes: {
|
||||
"k8s.cluster.name": clusterIdentifier,
|
||||
"resource.k8s.cluster.name": clusterIdentifier,
|
||||
},
|
||||
aggegationType: AggregationType.Avg,
|
||||
aggregateBy: {},
|
||||
@@ -114,7 +114,7 @@ const KubernetesClusterPods: FunctionComponent<
|
||||
filterData: {
|
||||
metricName: "k8s.pod.memory.usage",
|
||||
attributes: {
|
||||
"k8s.cluster.name": clusterIdentifier,
|
||||
"resource.k8s.cluster.name": clusterIdentifier,
|
||||
},
|
||||
aggegationType: AggregationType.Avg,
|
||||
aggregateBy: {},
|
||||
@@ -136,9 +136,10 @@ const KubernetesClusterPods: FunctionComponent<
|
||||
},
|
||||
metricQueryData: {
|
||||
filterData: {
|
||||
metricName: "k8s.pod.network.io.receive",
|
||||
metricName: "k8s.pod.network.io",
|
||||
attributes: {
|
||||
"k8s.cluster.name": clusterIdentifier,
|
||||
"resource.k8s.cluster.name": clusterIdentifier,
|
||||
"metricAttributes.direction": "receive",
|
||||
},
|
||||
aggegationType: AggregationType.Avg,
|
||||
aggregateBy: {},
|
||||
@@ -160,9 +161,10 @@ const KubernetesClusterPods: FunctionComponent<
|
||||
},
|
||||
metricQueryData: {
|
||||
filterData: {
|
||||
metricName: "k8s.pod.network.io.transmit",
|
||||
metricName: "k8s.pod.network.io",
|
||||
attributes: {
|
||||
"k8s.cluster.name": clusterIdentifier,
|
||||
"resource.k8s.cluster.name": clusterIdentifier,
|
||||
"metricAttributes.direction": "transmit",
|
||||
},
|
||||
aggegationType: AggregationType.Avg,
|
||||
aggregateBy: {},
|
||||
|
||||
@@ -9,11 +9,9 @@ import { Route as PageRoute, Routes } from "react-router-dom";
|
||||
|
||||
// Pages
|
||||
import ExceptionsUnresolved from "../Pages/Exceptions/Unresolved";
|
||||
|
||||
import ExceptionsResolved from "../Pages/Exceptions/Resolved";
|
||||
|
||||
import ExceptionsArchived from "../Pages/Exceptions/Archived";
|
||||
|
||||
import ExceptionsDocumentationPage from "../Pages/Exceptions/Documentation";
|
||||
import ExceptionView from "../Pages/Exceptions/View/Index";
|
||||
|
||||
const ExceptionsRoutes: FunctionComponent<ComponentProps> = (
|
||||
@@ -61,6 +59,17 @@ const ExceptionsRoutes: FunctionComponent<ComponentProps> = (
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<PageRoute
|
||||
path={ExceptionsRoutePath[PageMap.EXCEPTIONS_DOCUMENTATION] || ""}
|
||||
element={
|
||||
<ExceptionsDocumentationPage
|
||||
{...props}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.EXCEPTIONS_DOCUMENTATION] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</PageRoute>
|
||||
|
||||
{/* Exception View - separate from main layout */}
|
||||
|
||||
@@ -31,6 +31,11 @@ export function getExceptionsBreadcrumbs(
|
||||
"Exceptions",
|
||||
"View Exception",
|
||||
]),
|
||||
...BuildBreadcrumbLinksByTitles(PageMap.EXCEPTIONS_DOCUMENTATION, [
|
||||
"Project",
|
||||
"Exceptions",
|
||||
"Documentation",
|
||||
]),
|
||||
};
|
||||
return breadcrumpLinksMap[path];
|
||||
}
|
||||
|
||||
@@ -471,6 +471,7 @@ enum PageMap {
|
||||
EXCEPTIONS_ARCHIVED = "EXCEPTIONS_ARCHIVED",
|
||||
EXCEPTIONS_VIEW_ROOT = "EXCEPTIONS_VIEW_ROOT",
|
||||
EXCEPTIONS_VIEW = "EXCEPTIONS_VIEW",
|
||||
EXCEPTIONS_DOCUMENTATION = "EXCEPTIONS_DOCUMENTATION",
|
||||
|
||||
// Push Logs in resource views
|
||||
}
|
||||
|
||||
@@ -117,6 +117,7 @@ export const ExceptionsRoutePath: Dictionary<string> = {
|
||||
[PageMap.EXCEPTIONS_ARCHIVED]: "archived",
|
||||
[PageMap.EXCEPTIONS_VIEW_ROOT]: "",
|
||||
[PageMap.EXCEPTIONS_VIEW]: `${RouteParams.ModelID}`,
|
||||
[PageMap.EXCEPTIONS_DOCUMENTATION]: "documentation",
|
||||
};
|
||||
|
||||
export const DashboardsRoutePath: Dictionary<string> = {
|
||||
@@ -2579,6 +2580,12 @@ const RouteMap: Dictionary<Route> = {
|
||||
ExceptionsRoutePath[PageMap.EXCEPTIONS_VIEW]
|
||||
}`,
|
||||
),
|
||||
|
||||
[PageMap.EXCEPTIONS_DOCUMENTATION]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/exceptions/${
|
||||
ExceptionsRoutePath[PageMap.EXCEPTIONS_DOCUMENTATION]
|
||||
}`,
|
||||
),
|
||||
};
|
||||
|
||||
export class RouteUtil {
|
||||
|
||||
@@ -39,7 +39,7 @@ const NavBarMenu: FunctionComponent<ComponentProps> = (
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="absolute left-0 z-10 mt-8 w-screen max-w-5xl transform px-2 sm:px-0">
|
||||
<div className="absolute left-0 z-50 mt-8 w-screen max-w-5xl transform px-2 sm:px-0">
|
||||
<div className="overflow-hidden rounded-2xl shadow-xl ring-1 ring-black ring-opacity-5 bg-white">
|
||||
{/* Sections */}
|
||||
<div className="p-6">
|
||||
@@ -127,7 +127,7 @@ const NavBarMenu: FunctionComponent<ComponentProps> = (
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`absolute left-1/2 z-10 mt-8 w-screen max-w-md -translate-x-1/2 transform px-2 sm:px-0 ${maxWidthClass}`}
|
||||
className={`absolute left-1/2 z-50 mt-8 w-screen max-w-md -translate-x-1/2 transform px-2 sm:px-0 ${maxWidthClass}`}
|
||||
>
|
||||
<div className="overflow-hidden rounded-2xl shadow-xl ring-1 ring-black ring-opacity-5 bg-white">
|
||||
{/* Menu Items */}
|
||||
|
||||
Reference in New Issue
Block a user