mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: implement MetricsDashboard and MetricsListPage components, update routing and descriptions for improved clarity
This commit is contained in:
@@ -0,0 +1,352 @@
|
|||||||
|
import React, {
|
||||||
|
Fragment,
|
||||||
|
FunctionComponent,
|
||||||
|
ReactElement,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import Service from "Common/Models/DatabaseModels/Service";
|
||||||
|
import MetricType from "Common/Models/DatabaseModels/MetricType";
|
||||||
|
import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
|
||||||
|
import ProjectUtil from "Common/UI/Utils/Project";
|
||||||
|
import API from "Common/UI/Utils/API/API";
|
||||||
|
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||||
|
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||||
|
import SortOrder from "Common/Types/BaseDatabase/SortOrder";
|
||||||
|
import ListResult from "Common/Types/BaseDatabase/ListResult";
|
||||||
|
import { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
|
||||||
|
import ObjectID from "Common/Types/ObjectID";
|
||||||
|
import ServiceElement from "../Service/ServiceElement";
|
||||||
|
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||||
|
import PageMap from "../../Utils/PageMap";
|
||||||
|
import Route from "Common/Types/API/Route";
|
||||||
|
import AppLink from "../AppLink/AppLink";
|
||||||
|
import Includes from "Common/Types/BaseDatabase/Includes";
|
||||||
|
|
||||||
|
interface ServiceMetricSummary {
|
||||||
|
service: Service;
|
||||||
|
metricCount: number;
|
||||||
|
metricNames: Array<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MetricsDashboard: FunctionComponent = (): ReactElement => {
|
||||||
|
const [serviceSummaries, setServiceSummaries] = useState<
|
||||||
|
Array<ServiceMetricSummary>
|
||||||
|
>([]);
|
||||||
|
const [totalMetricCount, setTotalMetricCount] = useState<number>(0);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||||
|
const [error, setError] = useState<string>("");
|
||||||
|
|
||||||
|
const loadDashboard: () => Promise<void> = async (): Promise<void> => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
setError("");
|
||||||
|
|
||||||
|
// Load services
|
||||||
|
const servicesResult: ListResult<Service> = await ModelAPI.getList({
|
||||||
|
modelType: Service,
|
||||||
|
query: {
|
||||||
|
projectId: ProjectUtil.getCurrentProjectId()!,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
serviceColor: true,
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
limit: LIMIT_PER_PROJECT,
|
||||||
|
skip: 0,
|
||||||
|
sort: {
|
||||||
|
name: SortOrder.Ascending,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const services: Array<Service> = servicesResult.data || [];
|
||||||
|
|
||||||
|
// Load all metric types with their services
|
||||||
|
const metricsResult: ListResult<MetricType> = await ModelAPI.getList({
|
||||||
|
modelType: MetricType,
|
||||||
|
query: {
|
||||||
|
projectId: ProjectUtil.getCurrentProjectId()!,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
unit: true,
|
||||||
|
description: true,
|
||||||
|
},
|
||||||
|
relationSelect: {
|
||||||
|
services: {
|
||||||
|
_id: true,
|
||||||
|
name: true,
|
||||||
|
serviceColor: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
limit: LIMIT_PER_PROJECT,
|
||||||
|
skip: 0,
|
||||||
|
sort: {
|
||||||
|
name: SortOrder.Ascending,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const metrics: Array<MetricType> = metricsResult.data || [];
|
||||||
|
setTotalMetricCount(metrics.length);
|
||||||
|
|
||||||
|
// Build per-service summaries
|
||||||
|
const summaryMap: Map<string, ServiceMetricSummary> = new Map();
|
||||||
|
|
||||||
|
for (const service of services) {
|
||||||
|
const serviceId: string = service.id?.toString() || "";
|
||||||
|
summaryMap.set(serviceId, {
|
||||||
|
service,
|
||||||
|
metricCount: 0,
|
||||||
|
metricNames: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const metric of metrics) {
|
||||||
|
const metricServices: Array<Service> = metric.services || [];
|
||||||
|
|
||||||
|
for (const metricService of metricServices) {
|
||||||
|
const serviceId: string =
|
||||||
|
metricService._id?.toString() ||
|
||||||
|
metricService.id?.toString() ||
|
||||||
|
"";
|
||||||
|
let summary: ServiceMetricSummary | undefined =
|
||||||
|
summaryMap.get(serviceId);
|
||||||
|
|
||||||
|
if (!summary) {
|
||||||
|
// Service exists in metric but wasn't in our services list
|
||||||
|
summary = {
|
||||||
|
service: metricService,
|
||||||
|
metricCount: 0,
|
||||||
|
metricNames: [],
|
||||||
|
};
|
||||||
|
summaryMap.set(serviceId, summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.metricCount += 1;
|
||||||
|
|
||||||
|
const metricName: string = metric.name || "";
|
||||||
|
if (metricName && summary.metricNames.length < 5) {
|
||||||
|
summary.metricNames.push(metricName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only show services that have metrics
|
||||||
|
const summariesWithData: Array<ServiceMetricSummary> = Array.from(
|
||||||
|
summaryMap.values(),
|
||||||
|
).filter((s: ServiceMetricSummary) => {
|
||||||
|
return s.metricCount > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort by metric count descending
|
||||||
|
summariesWithData.sort(
|
||||||
|
(a: ServiceMetricSummary, b: ServiceMetricSummary) => {
|
||||||
|
return b.metricCount - a.metricCount;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
setServiceSummaries(summariesWithData);
|
||||||
|
} catch (err) {
|
||||||
|
setError(API.getFriendlyMessage(err as Error));
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
void loadDashboard();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <PageLoader isVisible={true} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<ErrorMessage
|
||||||
|
message={error}
|
||||||
|
onRefreshClick={() => {
|
||||||
|
void loadDashboard();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serviceSummaries.length === 0) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-lg border border-gray-200 bg-white p-12 text-center">
|
||||||
|
<div className="text-gray-400 text-5xl mb-4">
|
||||||
|
<svg
|
||||||
|
className="mx-auto h-16 w-16 text-indigo-200"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 48 48"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
d="M6 38 L6 20 L12 20 L12 38"
|
||||||
|
fill="currentColor"
|
||||||
|
opacity={0.4}
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
d="M16 38 L16 14 L22 14 L22 38"
|
||||||
|
fill="currentColor"
|
||||||
|
opacity={0.6}
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
d="M26 38 L26 24 L32 24 L32 38"
|
||||||
|
fill="currentColor"
|
||||||
|
opacity={0.5}
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
d="M36 38 L36 10 L42 10 L42 38"
|
||||||
|
fill="currentColor"
|
||||||
|
opacity={0.8}
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
d="M4 38 L44 38"
|
||||||
|
opacity={0.3}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||||||
|
No metrics data yet
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-500 max-w-md mx-auto">
|
||||||
|
Once your services start sending metrics via OpenTelemetry, you{"'"}ll
|
||||||
|
see a summary of which services are reporting, what metrics they
|
||||||
|
collect, and more.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
{/* Summary Stats */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
||||||
|
<div className="rounded-lg border border-gray-200 bg-white p-5">
|
||||||
|
<p className="text-sm text-gray-500">Total Metrics</p>
|
||||||
|
<p className="text-3xl font-bold text-gray-900 mt-1">
|
||||||
|
{totalMetricCount}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg border border-gray-200 bg-white p-5">
|
||||||
|
<p className="text-sm text-gray-500">Services Reporting</p>
|
||||||
|
<p className="text-3xl font-bold text-gray-900 mt-1">
|
||||||
|
{serviceSummaries.length}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg border border-gray-200 bg-white p-5">
|
||||||
|
<p className="text-sm text-gray-500">Avg Metrics per Service</p>
|
||||||
|
<p className="text-3xl font-bold text-gray-900 mt-1">
|
||||||
|
{serviceSummaries.length > 0
|
||||||
|
? Math.round(totalMetricCount / serviceSummaries.length)
|
||||||
|
: 0}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Service Cards */}
|
||||||
|
<div className="mb-8">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900">
|
||||||
|
Services Reporting Metrics
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-500 mt-1">
|
||||||
|
Each service and the metrics it collects
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<AppLink
|
||||||
|
className="text-sm text-indigo-600 hover:text-indigo-800 font-medium"
|
||||||
|
to={RouteUtil.populateRouteParams(
|
||||||
|
RouteMap[PageMap.METRICS_LIST] as Route,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
View all metrics
|
||||||
|
</AppLink>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{serviceSummaries.map((summary: ServiceMetricSummary) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={summary.service.id?.toString() || summary.service._id?.toString()}
|
||||||
|
className="rounded-lg border border-gray-200 bg-white p-5 hover:shadow-md transition-shadow"
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between mb-3">
|
||||||
|
<ServiceElement service={summary.service} />
|
||||||
|
<span className="text-xs bg-green-100 text-green-800 px-2 py-0.5 rounded-full font-medium">
|
||||||
|
Active
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-3">
|
||||||
|
<p className="text-xs text-gray-500">Metrics Collected</p>
|
||||||
|
<p className="text-lg font-semibold text-gray-900">
|
||||||
|
{summary.metricCount}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-gray-500 mb-1.5">Sample Metrics</p>
|
||||||
|
<div className="flex flex-wrap gap-1.5">
|
||||||
|
{summary.metricNames.map((name: string) => {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
key={name}
|
||||||
|
className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-blue-50 text-blue-700"
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{summary.metricCount > summary.metricNames.length && (
|
||||||
|
<span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-500">
|
||||||
|
+{summary.metricCount - summary.metricNames.length} more
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4 pt-3 border-t border-gray-100">
|
||||||
|
<AppLink
|
||||||
|
className="text-sm text-indigo-600 hover:text-indigo-800 font-medium"
|
||||||
|
to={RouteUtil.populateRouteParams(
|
||||||
|
RouteMap[PageMap.SERVICE_VIEW_METRICS] as Route,
|
||||||
|
{
|
||||||
|
modelId: new ObjectID(
|
||||||
|
(summary.service._id as string) ||
|
||||||
|
summary.service.id?.toString() ||
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
View service metrics
|
||||||
|
</AppLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MetricsDashboard;
|
||||||
@@ -43,9 +43,9 @@ const MetricsTable: FunctionComponent<ComponentProps> = (
|
|||||||
sortBy="name"
|
sortBy="name"
|
||||||
sortOrder={SortOrder.Ascending}
|
sortOrder={SortOrder.Ascending}
|
||||||
cardProps={{
|
cardProps={{
|
||||||
title: "Metrics",
|
title: "All Metrics",
|
||||||
description:
|
description:
|
||||||
"Metrics are the individual data points that make up a service. They are the building blocks of a service and represent the work done by a single service.",
|
"All metrics collected from your services. Click on a metric to explore its data in the chart viewer.",
|
||||||
}}
|
}}
|
||||||
onViewPage={async (item: MetricType) => {
|
onViewPage={async (item: MetricType) => {
|
||||||
const route: Route = RouteUtil.populateRouteParams(
|
const route: Route = RouteUtil.populateRouteParams(
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ const DashboardNavbar: FunctionComponent<ComponentProps> = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Metrics",
|
title: "Metrics",
|
||||||
description: "Monitor system metrics.",
|
description: "Monitor and visualize system metrics across your services.",
|
||||||
route: RouteUtil.populateRouteParams(RouteMap[PageMap.METRICS] as Route),
|
route: RouteUtil.populateRouteParams(RouteMap[PageMap.METRICS] as Route),
|
||||||
activeRoute: RouteMap[PageMap.METRICS],
|
activeRoute: RouteMap[PageMap.METRICS],
|
||||||
icon: IconProp.Heartbeat,
|
icon: IconProp.Heartbeat,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import React, {
|
|||||||
useEffect,
|
useEffect,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import MetricsTable from "../../Components/Metrics/MetricsTable";
|
import MetricsDashboard from "../../Components/Metrics/MetricsDashboard";
|
||||||
import Service from "Common/Models/DatabaseModels/Service";
|
import Service from "Common/Models/DatabaseModels/Service";
|
||||||
import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
|
import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
|
||||||
import API from "Common/UI/Utils/API/API";
|
import API from "Common/UI/Utils/API/API";
|
||||||
@@ -62,7 +62,7 @@ const MetricsPage: FunctionComponent<PageComponentProps> = (
|
|||||||
return <TelemetryDocumentation telemetryType="metrics" />;
|
return <TelemetryDocumentation telemetryType="metrics" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <MetricsTable />;
|
return <MetricsDashboard />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MetricsPage;
|
export default MetricsPage;
|
||||||
|
|||||||
8
App/FeatureSet/Dashboard/src/Pages/Metrics/List.tsx
Normal file
8
App/FeatureSet/Dashboard/src/Pages/Metrics/List.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import React, { FunctionComponent, ReactElement } from "react";
|
||||||
|
import MetricsTable from "../../Components/Metrics/MetricsTable";
|
||||||
|
|
||||||
|
const MetricsListPage: FunctionComponent = (): ReactElement => {
|
||||||
|
return <MetricsTable />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MetricsListPage;
|
||||||
@@ -14,21 +14,30 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
link: {
|
link: {
|
||||||
title: "All Metrics",
|
title: "Overview",
|
||||||
to: RouteUtil.populateRouteParams(
|
to: RouteUtil.populateRouteParams(
|
||||||
RouteMap[PageMap.METRICS] as Route,
|
RouteMap[PageMap.METRICS] as Route,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
icon: IconProp.Home,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: {
|
||||||
|
title: "All Metrics",
|
||||||
|
to: RouteUtil.populateRouteParams(
|
||||||
|
RouteMap[PageMap.METRICS_LIST] as Route,
|
||||||
|
),
|
||||||
|
},
|
||||||
icon: IconProp.ChartBar,
|
icon: IconProp.ChartBar,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Documentation",
|
title: "Help",
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
link: {
|
link: {
|
||||||
title: "Documentation",
|
title: "Setup Guide",
|
||||||
to: RouteUtil.populateRouteParams(
|
to: RouteUtil.populateRouteParams(
|
||||||
RouteMap[PageMap.METRICS_DOCUMENTATION] as Route,
|
RouteMap[PageMap.METRICS_DOCUMENTATION] as Route,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const MetricsViewLayout: FunctionComponent<
|
|||||||
const path: string = Navigation.getRoutePath(RouteUtil.getRoutes());
|
const path: string = Navigation.getRoutePath(RouteUtil.getRoutes());
|
||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
title="Metrics Explorer"
|
title="Metric Explorer"
|
||||||
breadcrumbLinks={getMetricsBreadcrumbs(path)}
|
breadcrumbLinks={getMetricsBreadcrumbs(path)}
|
||||||
>
|
>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Route as PageRoute, Routes } from "react-router-dom";
|
|||||||
|
|
||||||
// Pages
|
// Pages
|
||||||
import MetricsPage from "../Pages/Metrics/Index";
|
import MetricsPage from "../Pages/Metrics/Index";
|
||||||
|
import MetricsListPage from "../Pages/Metrics/List";
|
||||||
import MetricsDocumentationPage from "../Pages/Metrics/Documentation";
|
import MetricsDocumentationPage from "../Pages/Metrics/Documentation";
|
||||||
|
|
||||||
import MetricViewPage from "../Pages/Metrics/View/Index";
|
import MetricViewPage from "../Pages/Metrics/View/Index";
|
||||||
@@ -28,6 +29,12 @@ const MetricsRoutes: FunctionComponent<ComponentProps> = (
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<PageRoute
|
||||||
|
path={MetricsRoutePath[PageMap.METRICS_LIST] || ""}
|
||||||
|
element={
|
||||||
|
<MetricsListPage />
|
||||||
|
}
|
||||||
|
/>
|
||||||
<PageRoute
|
<PageRoute
|
||||||
path={MetricsRoutePath[PageMap.METRICS_DOCUMENTATION] || ""}
|
path={MetricsRoutePath[PageMap.METRICS_DOCUMENTATION] || ""}
|
||||||
element={
|
element={
|
||||||
|
|||||||
@@ -6,15 +6,20 @@ import Link from "Common/Types/Link";
|
|||||||
export function getMetricsBreadcrumbs(path: string): Array<Link> | undefined {
|
export function getMetricsBreadcrumbs(path: string): Array<Link> | undefined {
|
||||||
const breadcrumpLinksMap: Dictionary<Link[]> = {
|
const breadcrumpLinksMap: Dictionary<Link[]> = {
|
||||||
...BuildBreadcrumbLinksByTitles(PageMap.METRICS, ["Project", "Metrics"]),
|
...BuildBreadcrumbLinksByTitles(PageMap.METRICS, ["Project", "Metrics"]),
|
||||||
|
...BuildBreadcrumbLinksByTitles(PageMap.METRICS_LIST, [
|
||||||
|
"Project",
|
||||||
|
"Metrics",
|
||||||
|
"All Metrics",
|
||||||
|
]),
|
||||||
...BuildBreadcrumbLinksByTitles(PageMap.METRIC_VIEW, [
|
...BuildBreadcrumbLinksByTitles(PageMap.METRIC_VIEW, [
|
||||||
"Project",
|
"Project",
|
||||||
"Metrics",
|
"Metrics",
|
||||||
"Metrics Explorer",
|
"Metric Explorer",
|
||||||
]),
|
]),
|
||||||
...BuildBreadcrumbLinksByTitles(PageMap.METRICS_DOCUMENTATION, [
|
...BuildBreadcrumbLinksByTitles(PageMap.METRICS_DOCUMENTATION, [
|
||||||
"Project",
|
"Project",
|
||||||
"Metrics",
|
"Metrics",
|
||||||
"Documentation",
|
"Setup Guide",
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
return breadcrumpLinksMap[path];
|
return breadcrumpLinksMap[path];
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ enum PageMap {
|
|||||||
// Metrics (standalone product)
|
// Metrics (standalone product)
|
||||||
METRICS_ROOT = "METRICS_ROOT",
|
METRICS_ROOT = "METRICS_ROOT",
|
||||||
METRICS = "METRICS",
|
METRICS = "METRICS",
|
||||||
|
METRICS_LIST = "METRICS_LIST",
|
||||||
METRIC_VIEW = "METRIC_VIEW",
|
METRIC_VIEW = "METRIC_VIEW",
|
||||||
METRICS_DOCUMENTATION = "METRICS_DOCUMENTATION",
|
METRICS_DOCUMENTATION = "METRICS_DOCUMENTATION",
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ export const LogsRoutePath: Dictionary<string> = {
|
|||||||
// Metrics product routes
|
// Metrics product routes
|
||||||
export const MetricsRoutePath: Dictionary<string> = {
|
export const MetricsRoutePath: Dictionary<string> = {
|
||||||
[PageMap.METRICS]: "",
|
[PageMap.METRICS]: "",
|
||||||
|
[PageMap.METRICS_LIST]: "list",
|
||||||
[PageMap.METRIC_VIEW]: "view",
|
[PageMap.METRIC_VIEW]: "view",
|
||||||
[PageMap.METRICS_DOCUMENTATION]: "documentation",
|
[PageMap.METRICS_DOCUMENTATION]: "documentation",
|
||||||
};
|
};
|
||||||
@@ -2275,6 +2276,12 @@ const RouteMap: Dictionary<Route> = {
|
|||||||
|
|
||||||
[PageMap.METRICS]: new Route(`/dashboard/${RouteParams.ProjectID}/metrics`),
|
[PageMap.METRICS]: new Route(`/dashboard/${RouteParams.ProjectID}/metrics`),
|
||||||
|
|
||||||
|
[PageMap.METRICS_LIST]: new Route(
|
||||||
|
`/dashboard/${RouteParams.ProjectID}/metrics/${
|
||||||
|
MetricsRoutePath[PageMap.METRICS_LIST]
|
||||||
|
}`,
|
||||||
|
),
|
||||||
|
|
||||||
[PageMap.METRIC_VIEW]: new Route(
|
[PageMap.METRIC_VIEW]: new Route(
|
||||||
`/dashboard/${RouteParams.ProjectID}/metrics/${
|
`/dashboard/${RouteParams.ProjectID}/metrics/${
|
||||||
MetricsRoutePath[PageMap.METRIC_VIEW]
|
MetricsRoutePath[PageMap.METRIC_VIEW]
|
||||||
|
|||||||
Reference in New Issue
Block a user