@openchoreo/backstage-plugin-openchoreo-observability 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/README.md +22 -0
- package/dist/api/FinOpsAgentApi.esm.js +43 -0
- package/dist/api/FinOpsAgentApi.esm.js.map +1 -0
- package/dist/api/ObservabilityApi.esm.js +568 -0
- package/dist/api/ObservabilityApi.esm.js.map +1 -0
- package/dist/api/ObserverUrlCache.esm.js +59 -0
- package/dist/api/ObserverUrlCache.esm.js.map +1 -0
- package/dist/api/RCAAgentApi.esm.js +104 -0
- package/dist/api/RCAAgentApi.esm.js.map +1 -0
- package/dist/components/Alerts/AlertRow.esm.js +203 -0
- package/dist/components/Alerts/AlertRow.esm.js.map +1 -0
- package/dist/components/Alerts/AlertsActions.esm.js +77 -0
- package/dist/components/Alerts/AlertsActions.esm.js.map +1 -0
- package/dist/components/Alerts/AlertsFilter.esm.js +95 -0
- package/dist/components/Alerts/AlertsFilter.esm.js.map +1 -0
- package/dist/components/Alerts/AlertsTable.esm.js +63 -0
- package/dist/components/Alerts/AlertsTable.esm.js.map +1 -0
- package/dist/components/Alerts/ObservabilityAlertsPage.esm.js +251 -0
- package/dist/components/Alerts/ObservabilityAlertsPage.esm.js.map +1 -0
- package/dist/components/Alerts/types.esm.js +4 -0
- package/dist/components/Alerts/types.esm.js.map +1 -0
- package/dist/components/CostAnalysis/CostAnalysisPage.esm.js +141 -0
- package/dist/components/CostAnalysis/CostAnalysisPage.esm.js.map +1 -0
- package/dist/components/CostAnalysis/CostAnalysisReport.esm.js +128 -0
- package/dist/components/CostAnalysis/CostAnalysisReport.esm.js.map +1 -0
- package/dist/components/CostAnalysis/CostAnalysisReportView.esm.js +517 -0
- package/dist/components/CostAnalysis/CostAnalysisReportView.esm.js.map +1 -0
- package/dist/components/CostAnalysis/CostAnalysisTable.esm.js +149 -0
- package/dist/components/CostAnalysis/CostAnalysisTable.esm.js.map +1 -0
- package/dist/components/CostAnalysis/FinOpsApplyButton.esm.js +112 -0
- package/dist/components/CostAnalysis/FinOpsApplyButton.esm.js.map +1 -0
- package/dist/components/CostAnalysis/index.esm.js +2 -0
- package/dist/components/CostAnalysis/index.esm.js.map +1 -0
- package/dist/components/Incidents/IncidentRow.esm.js +258 -0
- package/dist/components/Incidents/IncidentRow.esm.js.map +1 -0
- package/dist/components/Incidents/IncidentsActions.esm.js +77 -0
- package/dist/components/Incidents/IncidentsActions.esm.js.map +1 -0
- package/dist/components/Incidents/IncidentsFilter.esm.js +144 -0
- package/dist/components/Incidents/IncidentsFilter.esm.js.map +1 -0
- package/dist/components/Incidents/IncidentsTable.esm.js +68 -0
- package/dist/components/Incidents/IncidentsTable.esm.js.map +1 -0
- package/dist/components/Incidents/ObservabilityProjectIncidentsPage.esm.js +283 -0
- package/dist/components/Incidents/ObservabilityProjectIncidentsPage.esm.js.map +1 -0
- package/dist/components/Incidents/types.esm.js +8 -0
- package/dist/components/Incidents/types.esm.js.map +1 -0
- package/dist/components/Metrics/ChartTooltip.esm.js +53 -0
- package/dist/components/Metrics/ChartTooltip.esm.js.map +1 -0
- package/dist/components/Metrics/HTTPMetricsSection.esm.js +109 -0
- package/dist/components/Metrics/HTTPMetricsSection.esm.js.map +1 -0
- package/dist/components/Metrics/MetricGraphByComponent.esm.js +99 -0
- package/dist/components/Metrics/MetricGraphByComponent.esm.js.map +1 -0
- package/dist/components/Metrics/MetricsActions.esm.js +30 -0
- package/dist/components/Metrics/MetricsActions.esm.js.map +1 -0
- package/dist/components/Metrics/MetricsFilters.esm.js +49 -0
- package/dist/components/Metrics/MetricsFilters.esm.js.map +1 -0
- package/dist/components/Metrics/ObservabilityMetricsPage.esm.js +196 -0
- package/dist/components/Metrics/ObservabilityMetricsPage.esm.js.map +1 -0
- package/dist/components/Metrics/styles.esm.js +55 -0
- package/dist/components/Metrics/styles.esm.js.map +1 -0
- package/dist/components/Metrics/utils.esm.js +221 -0
- package/dist/components/Metrics/utils.esm.js.map +1 -0
- package/dist/components/RCA/RCAActions.esm.js +42 -0
- package/dist/components/RCA/RCAActions.esm.js.map +1 -0
- package/dist/components/RCA/RCAFilters.esm.js +100 -0
- package/dist/components/RCA/RCAFilters.esm.js.map +1 -0
- package/dist/components/RCA/RCAPage.esm.js +137 -0
- package/dist/components/RCA/RCAPage.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/EntityLinkContext.esm.js +9 -0
- package/dist/components/RCA/RCAReport/EntityLinkContext.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/FormattedText.esm.js +94 -0
- package/dist/components/RCA/RCAReport/FormattedText.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/RCAReportView.esm.js +308 -0
- package/dist/components/RCA/RCAReport/RCAReportView.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/AssessmentSection.esm.js +113 -0
- package/dist/components/RCA/RCAReport/sections/AssessmentSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/ChatPanelSection.esm.js +272 -0
- package/dist/components/RCA/RCAReport/sections/ChatPanelSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/ExcludedCausesSection.esm.js +48 -0
- package/dist/components/RCA/RCAReport/sections/ExcludedCausesSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/IncidentOverviewSection.esm.js +205 -0
- package/dist/components/RCA/RCAReport/sections/IncidentOverviewSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/InvestigationPathSection.esm.js +121 -0
- package/dist/components/RCA/RCAReport/sections/InvestigationPathSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/PatchTabContent.esm.js +593 -0
- package/dist/components/RCA/RCAReport/sections/PatchTabContent.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/QuickFixesPanelSection.esm.js +35 -0
- package/dist/components/RCA/RCAReport/sections/QuickFixesPanelSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/RecommendationsSection.esm.js +54 -0
- package/dist/components/RCA/RCAReport/sections/RecommendationsSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/RootCausesSection.esm.js +526 -0
- package/dist/components/RCA/RCAReport/sections/RootCausesSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/SystemTimelineSection.esm.js +70 -0
- package/dist/components/RCA/RCAReport/sections/SystemTimelineSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/VisibilityImprovementsSection.esm.js +54 -0
- package/dist/components/RCA/RCAReport/sections/VisibilityImprovementsSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/styles.esm.js +467 -0
- package/dist/components/RCA/RCAReport/styles.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport.esm.js +109 -0
- package/dist/components/RCA/RCAReport.esm.js.map +1 -0
- package/dist/components/RCA/RCATable.esm.js +140 -0
- package/dist/components/RCA/RCATable.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/LogEntry.esm.js +182 -0
- package/dist/components/RuntimeLogs/LogEntry.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/LogsActions.esm.js +106 -0
- package/dist/components/RuntimeLogs/LogsActions.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/LogsFilter.esm.js +200 -0
- package/dist/components/RuntimeLogs/LogsFilter.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/LogsTable.esm.js +91 -0
- package/dist/components/RuntimeLogs/LogsTable.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.esm.js +220 -0
- package/dist/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/ObservabilityRuntimeLogsPage.esm.js +222 -0
- package/dist/components/RuntimeLogs/ObservabilityRuntimeLogsPage.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/styles.esm.js +256 -0
- package/dist/components/RuntimeLogs/styles.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/types.esm.js +16 -0
- package/dist/components/RuntimeLogs/types.esm.js.map +1 -0
- package/dist/components/TimeRangeFilter/TimeRangeFilter.esm.js +280 -0
- package/dist/components/TimeRangeFilter/TimeRangeFilter.esm.js.map +1 -0
- package/dist/components/Traces/ObservabilityTracesPage.esm.js +165 -0
- package/dist/components/Traces/ObservabilityTracesPage.esm.js.map +1 -0
- package/dist/components/Traces/TracesActions.esm.js +37 -0
- package/dist/components/Traces/TracesActions.esm.js.map +1 -0
- package/dist/components/Traces/TracesFilters.esm.js +121 -0
- package/dist/components/Traces/TracesFilters.esm.js.map +1 -0
- package/dist/components/Traces/TracesTable.esm.js +137 -0
- package/dist/components/Traces/TracesTable.esm.js.map +1 -0
- package/dist/components/Traces/WaterfallView.esm.js +567 -0
- package/dist/components/Traces/WaterfallView.esm.js.map +1 -0
- package/dist/components/Traces/styles.esm.js +198 -0
- package/dist/components/Traces/styles.esm.js.map +1 -0
- package/dist/components/Traces/utils.esm.js +19 -0
- package/dist/components/Traces/utils.esm.js.map +1 -0
- package/dist/hooks/useAutoSelectFirstEnvironment.esm.js +25 -0
- package/dist/hooks/useAutoSelectFirstEnvironment.esm.js.map +1 -0
- package/dist/hooks/useComponentAlerts.esm.js +80 -0
- package/dist/hooks/useComponentAlerts.esm.js.map +1 -0
- package/dist/hooks/useDataPlaneNetPolProvider.esm.js +55 -0
- package/dist/hooks/useDataPlaneNetPolProvider.esm.js.map +1 -0
- package/dist/hooks/useDebouncedSearch.esm.js +24 -0
- package/dist/hooks/useDebouncedSearch.esm.js.map +1 -0
- package/dist/hooks/useFilters.esm.js +29 -0
- package/dist/hooks/useFilters.esm.js.map +1 -0
- package/dist/hooks/useFinOpsReport.esm.js +66 -0
- package/dist/hooks/useFinOpsReport.esm.js.map +1 -0
- package/dist/hooks/useFinOpsReports.esm.js +70 -0
- package/dist/hooks/useFinOpsReports.esm.js.map +1 -0
- package/dist/hooks/useGetComponentsByProject.esm.js +57 -0
- package/dist/hooks/useGetComponentsByProject.esm.js.map +1 -0
- package/dist/hooks/useGetEnvironmentsByNamespace.esm.js +53 -0
- package/dist/hooks/useGetEnvironmentsByNamespace.esm.js.map +1 -0
- package/dist/hooks/useGetNamespaceAndProjectByEntity.esm.js +40 -0
- package/dist/hooks/useGetNamespaceAndProjectByEntity.esm.js.map +1 -0
- package/dist/hooks/useMetrics.esm.js +107 -0
- package/dist/hooks/useMetrics.esm.js.map +1 -0
- package/dist/hooks/useProjectIncidents.esm.js +111 -0
- package/dist/hooks/useProjectIncidents.esm.js.map +1 -0
- package/dist/hooks/useProjectRuntimeLogs.esm.js +185 -0
- package/dist/hooks/useProjectRuntimeLogs.esm.js.map +1 -0
- package/dist/hooks/useRCAReportByAlert.esm.js +54 -0
- package/dist/hooks/useRCAReportByAlert.esm.js.map +1 -0
- package/dist/hooks/useRCAReports.esm.js +75 -0
- package/dist/hooks/useRCAReports.esm.js.map +1 -0
- package/dist/hooks/useRuntimeLogs.esm.js +147 -0
- package/dist/hooks/useRuntimeLogs.esm.js.map +1 -0
- package/dist/hooks/useSpanDetails.esm.js +71 -0
- package/dist/hooks/useSpanDetails.esm.js.map +1 -0
- package/dist/hooks/useTraceSpans.esm.js +80 -0
- package/dist/hooks/useTraceSpans.esm.js.map +1 -0
- package/dist/hooks/useTraces.esm.js +112 -0
- package/dist/hooks/useTraces.esm.js.map +1 -0
- package/dist/hooks/useUpdateIncident.esm.js +50 -0
- package/dist/hooks/useUpdateIncident.esm.js.map +1 -0
- package/dist/hooks/useUrlFilters.esm.js +63 -0
- package/dist/hooks/useUrlFilters.esm.js.map +1 -0
- package/dist/hooks/useUrlFiltersForAlerts.esm.js +80 -0
- package/dist/hooks/useUrlFiltersForAlerts.esm.js.map +1 -0
- package/dist/hooks/useUrlFiltersForIncidents.esm.js +87 -0
- package/dist/hooks/useUrlFiltersForIncidents.esm.js.map +1 -0
- package/dist/hooks/useUrlFiltersForRuntimeLogs.esm.js +135 -0
- package/dist/hooks/useUrlFiltersForRuntimeLogs.esm.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/plugin.esm.js +107 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/routes.esm.js +8 -0
- package/dist/routes.esm.js.map +1 -0
- package/dist/types.esm.js +18 -0
- package/dist/types.esm.js.map +1 -0
- package/dist/utils/applyResourceChange.esm.js +79 -0
- package/dist/utils/applyResourceChange.esm.js.map +1 -0
- package/dist/utils/urlTimeRange.esm.js +52 -0
- package/dist/utils/urlTimeRange.esm.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { Box, Grid, Card, CardHeader, Divider, CardContent, Typography, Button } from '@material-ui/core';
|
|
4
|
+
import { Progress } from '@backstage/core-components';
|
|
5
|
+
import { MetricsFilters } from './MetricsFilters.esm.js';
|
|
6
|
+
import { MetricGraphByComponent } from './MetricGraphByComponent.esm.js';
|
|
7
|
+
import { MetricsActions } from './MetricsActions.esm.js';
|
|
8
|
+
import { HTTPMetricsSection } from './HTTPMetricsSection.esm.js';
|
|
9
|
+
import { useGetNamespaceAndProjectByEntity } from '../../hooks/useGetNamespaceAndProjectByEntity.esm.js';
|
|
10
|
+
import '@backstage/core-plugin-api';
|
|
11
|
+
import { useGetEnvironmentsByNamespace } from '../../hooks/useGetEnvironmentsByNamespace.esm.js';
|
|
12
|
+
import { useUrlFilters } from '../../hooks/useUrlFilters.esm.js';
|
|
13
|
+
import { useMetrics } from '../../hooks/useMetrics.esm.js';
|
|
14
|
+
import '../../api/ObservabilityApi.esm.js';
|
|
15
|
+
import '@openchoreo/backstage-plugin-common';
|
|
16
|
+
import { useMetricsPermission, ForbiddenState } from '@openchoreo/backstage-plugin-react';
|
|
17
|
+
import { useEntity } from '@backstage/plugin-catalog-react';
|
|
18
|
+
import 'react-router-dom';
|
|
19
|
+
import '../../utils/urlTimeRange.esm.js';
|
|
20
|
+
import '../../api/RCAAgentApi.esm.js';
|
|
21
|
+
import '../../api/FinOpsAgentApi.esm.js';
|
|
22
|
+
import { useObservabilityMetricsPageStyles } from './styles.esm.js';
|
|
23
|
+
import { Alert } from '@material-ui/lab';
|
|
24
|
+
|
|
25
|
+
const ObservabilityMetricsContent = () => {
|
|
26
|
+
const classes = useObservabilityMetricsPageStyles();
|
|
27
|
+
const { entity } = useEntity();
|
|
28
|
+
const {
|
|
29
|
+
namespace,
|
|
30
|
+
project,
|
|
31
|
+
error: namespaceError
|
|
32
|
+
} = useGetNamespaceAndProjectByEntity(entity);
|
|
33
|
+
const {
|
|
34
|
+
environments,
|
|
35
|
+
loading: environmentsLoading,
|
|
36
|
+
error: environmentsError
|
|
37
|
+
} = useGetEnvironmentsByNamespace(namespace, project);
|
|
38
|
+
const { filters, updateFilters } = useUrlFilters({
|
|
39
|
+
environments
|
|
40
|
+
});
|
|
41
|
+
const {
|
|
42
|
+
canViewMetrics: canViewMetricsForEnv,
|
|
43
|
+
loading: envPermissionLoading,
|
|
44
|
+
deniedTooltip: envPermissionDenied,
|
|
45
|
+
permissionName: envPermissionName
|
|
46
|
+
} = useMetricsPermission(filters.environment?.name);
|
|
47
|
+
const {
|
|
48
|
+
metrics,
|
|
49
|
+
loading: metricsLoading,
|
|
50
|
+
error: metricsError,
|
|
51
|
+
fetchMetrics,
|
|
52
|
+
refresh
|
|
53
|
+
} = useMetrics(filters, entity, namespace, project);
|
|
54
|
+
const resourceMetrics = metrics;
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (filters.environment && filters.timeRange && canViewMetricsForEnv) {
|
|
57
|
+
fetchMetrics(true);
|
|
58
|
+
}
|
|
59
|
+
}, [
|
|
60
|
+
filters.environment,
|
|
61
|
+
filters.timeRange,
|
|
62
|
+
fetchMetrics,
|
|
63
|
+
canViewMetricsForEnv
|
|
64
|
+
]);
|
|
65
|
+
const handleFiltersChange = (newFilters) => {
|
|
66
|
+
updateFilters(newFilters);
|
|
67
|
+
};
|
|
68
|
+
const handleRefresh = () => {
|
|
69
|
+
refresh();
|
|
70
|
+
};
|
|
71
|
+
if (namespaceError) {
|
|
72
|
+
return /* @__PURE__ */ jsx(Fragment, {});
|
|
73
|
+
}
|
|
74
|
+
if (environmentsError) {
|
|
75
|
+
return /* @__PURE__ */ jsx(Fragment, {});
|
|
76
|
+
}
|
|
77
|
+
const isLoading = environmentsLoading;
|
|
78
|
+
const renderError = (error) => {
|
|
79
|
+
const isObservabilityDisabled = error.includes(
|
|
80
|
+
"Observability is not enabled"
|
|
81
|
+
);
|
|
82
|
+
return /* @__PURE__ */ jsxs(
|
|
83
|
+
Alert,
|
|
84
|
+
{
|
|
85
|
+
severity: isObservabilityDisabled ? "info" : "error",
|
|
86
|
+
className: classes.errorContainer,
|
|
87
|
+
children: [
|
|
88
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", children: isObservabilityDisabled ? "Observability is not enabled for this component in the current environment. Enable observability to view metrics." : error }),
|
|
89
|
+
!isObservabilityDisabled && /* @__PURE__ */ jsx(Button, { onClick: handleRefresh, color: "inherit", size: "small", children: "Retry" })
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
95
|
+
(isLoading || metricsLoading) && /* @__PURE__ */ jsx(Progress, {}),
|
|
96
|
+
!isLoading && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
97
|
+
/* @__PURE__ */ jsx(
|
|
98
|
+
MetricsFilters,
|
|
99
|
+
{
|
|
100
|
+
filters,
|
|
101
|
+
onFiltersChange: handleFiltersChange,
|
|
102
|
+
environments,
|
|
103
|
+
disabled: isLoading
|
|
104
|
+
}
|
|
105
|
+
),
|
|
106
|
+
filters.environment && !envPermissionLoading && !canViewMetricsForEnv && /* @__PURE__ */ jsx(
|
|
107
|
+
ForbiddenState,
|
|
108
|
+
{
|
|
109
|
+
message: envPermissionDenied,
|
|
110
|
+
permissionName: envPermissionName,
|
|
111
|
+
variant: "compact"
|
|
112
|
+
}
|
|
113
|
+
),
|
|
114
|
+
canViewMetricsForEnv && metricsError && renderError(metricsError),
|
|
115
|
+
canViewMetricsForEnv && /* @__PURE__ */ jsx(
|
|
116
|
+
MetricsActions,
|
|
117
|
+
{
|
|
118
|
+
onRefresh: handleRefresh,
|
|
119
|
+
disabled: metricsLoading
|
|
120
|
+
}
|
|
121
|
+
),
|
|
122
|
+
canViewMetricsForEnv && /* @__PURE__ */ jsxs(
|
|
123
|
+
Grid,
|
|
124
|
+
{
|
|
125
|
+
container: true,
|
|
126
|
+
spacing: 4,
|
|
127
|
+
className: classes.metricsGridContainer,
|
|
128
|
+
children: [
|
|
129
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsxs(Card, { children: [
|
|
130
|
+
/* @__PURE__ */ jsx(CardHeader, { title: "CPU Usage" }),
|
|
131
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
132
|
+
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(
|
|
133
|
+
MetricGraphByComponent,
|
|
134
|
+
{
|
|
135
|
+
usageData: resourceMetrics?.cpuUsage || {},
|
|
136
|
+
usageType: "cpu",
|
|
137
|
+
timeRange: filters.timeRange,
|
|
138
|
+
customStartTime: filters.customStartTime,
|
|
139
|
+
customEndTime: filters.customEndTime
|
|
140
|
+
}
|
|
141
|
+
) })
|
|
142
|
+
] }) }),
|
|
143
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsxs(Card, { children: [
|
|
144
|
+
/* @__PURE__ */ jsx(CardHeader, { title: "Memory Usage" }),
|
|
145
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
146
|
+
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(
|
|
147
|
+
MetricGraphByComponent,
|
|
148
|
+
{
|
|
149
|
+
usageData: resourceMetrics?.memoryUsage || {},
|
|
150
|
+
usageType: "memory",
|
|
151
|
+
timeRange: filters.timeRange,
|
|
152
|
+
customStartTime: filters.customStartTime,
|
|
153
|
+
customEndTime: filters.customEndTime
|
|
154
|
+
}
|
|
155
|
+
) })
|
|
156
|
+
] }) }),
|
|
157
|
+
/* @__PURE__ */ jsx(
|
|
158
|
+
HTTPMetricsSection,
|
|
159
|
+
{
|
|
160
|
+
filters,
|
|
161
|
+
entity,
|
|
162
|
+
namespaceName: namespace,
|
|
163
|
+
project
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
] })
|
|
170
|
+
] });
|
|
171
|
+
};
|
|
172
|
+
const ObservabilityMetricsPage = () => {
|
|
173
|
+
const {
|
|
174
|
+
canViewMetrics,
|
|
175
|
+
loading: permissionLoading,
|
|
176
|
+
deniedTooltip,
|
|
177
|
+
permissionName
|
|
178
|
+
} = useMetricsPermission();
|
|
179
|
+
if (permissionLoading) {
|
|
180
|
+
return /* @__PURE__ */ jsx(Progress, {});
|
|
181
|
+
}
|
|
182
|
+
if (!canViewMetrics) {
|
|
183
|
+
return /* @__PURE__ */ jsx(
|
|
184
|
+
ForbiddenState,
|
|
185
|
+
{
|
|
186
|
+
message: deniedTooltip,
|
|
187
|
+
permissionName,
|
|
188
|
+
variant: "fullpage"
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
return /* @__PURE__ */ jsx(ObservabilityMetricsContent, {});
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export { ObservabilityMetricsPage };
|
|
196
|
+
//# sourceMappingURL=ObservabilityMetricsPage.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ObservabilityMetricsPage.esm.js","sources":["../../../src/components/Metrics/ObservabilityMetricsPage.tsx"],"sourcesContent":["import { useEffect } from 'react';\nimport {\n Grid,\n Card,\n CardContent,\n CardHeader,\n Divider,\n Button,\n Typography,\n Box,\n} from '@material-ui/core';\nimport { Progress } from '@backstage/core-components';\nimport { MetricsFilters } from './MetricsFilters';\nimport { MetricGraphByComponent } from './MetricGraphByComponent';\nimport { MetricsActions } from './MetricsActions';\nimport { HTTPMetricsSection } from './HTTPMetricsSection';\nimport {\n useGetNamespaceAndProjectByEntity,\n useGetEnvironmentsByNamespace,\n useUrlFilters,\n useMetrics,\n} from '../../hooks';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport {\n ResourceMetrics,\n CpuUsageMetrics,\n MemoryUsageMetrics,\n} from '../../types';\nimport { useObservabilityMetricsPageStyles } from './styles';\nimport { Alert } from '@material-ui/lab';\nimport {\n useMetricsPermission,\n ForbiddenState,\n} from '@openchoreo/backstage-plugin-react';\n\nconst ObservabilityMetricsContent = () => {\n const classes = useObservabilityMetricsPageStyles();\n const { entity } = useEntity();\n\n const {\n namespace,\n project,\n error: namespaceError,\n } = useGetNamespaceAndProjectByEntity(entity);\n const {\n environments,\n loading: environmentsLoading,\n error: environmentsError,\n } = useGetEnvironmentsByNamespace(namespace, project);\n\n // URL-synced filters - must be after environments are available\n const { filters, updateFilters } = useUrlFilters({\n environments,\n });\n\n // Per-environment permission (ABAC `resource.environment`) — gates the\n // metrics content and the fetch once an env is selected. See openchoreo#3408.\n const {\n canViewMetrics: canViewMetricsForEnv,\n loading: envPermissionLoading,\n deniedTooltip: envPermissionDenied,\n permissionName: envPermissionName,\n } = useMetricsPermission(filters.environment?.name);\n\n // Fetch metrics using the custom hook\n const {\n metrics,\n loading: metricsLoading,\n error: metricsError,\n fetchMetrics,\n refresh,\n } = useMetrics(filters, entity, namespace as string, project as string);\n const resourceMetrics = metrics as ResourceMetrics;\n\n // Fetch metrics when filters change\n useEffect(() => {\n if (filters.environment && filters.timeRange && canViewMetricsForEnv) {\n fetchMetrics(true);\n }\n }, [\n filters.environment,\n filters.timeRange,\n fetchMetrics,\n canViewMetricsForEnv,\n ]);\n\n const handleFiltersChange = (newFilters: Partial<typeof filters>) => {\n updateFilters(newFilters);\n };\n\n const handleRefresh = () => {\n refresh();\n };\n\n if (namespaceError) {\n // TODO: Add a toast notification here\n return <></>;\n }\n\n if (environmentsError) {\n // TODO: Add a toast notification here\n return <></>;\n }\n\n const isLoading = environmentsLoading;\n\n const renderError = (error: string) => {\n const isObservabilityDisabled = error.includes(\n 'Observability is not enabled',\n );\n\n return (\n <Alert\n severity={isObservabilityDisabled ? 'info' : 'error'}\n className={classes.errorContainer}\n >\n <Typography variant=\"body1\">\n {isObservabilityDisabled\n ? 'Observability is not enabled for this component in the current environment. Enable observability to view metrics.'\n : error}\n </Typography>\n {!isObservabilityDisabled && (\n <Button onClick={handleRefresh} color=\"inherit\" size=\"small\">\n Retry\n </Button>\n )}\n </Alert>\n );\n };\n\n return (\n <Box>\n {(isLoading || metricsLoading) && <Progress />}\n\n {!isLoading && (\n <>\n <MetricsFilters\n filters={filters}\n onFiltersChange={handleFiltersChange}\n environments={environments}\n disabled={isLoading}\n />\n {filters.environment &&\n !envPermissionLoading &&\n !canViewMetricsForEnv && (\n <ForbiddenState\n message={envPermissionDenied}\n permissionName={envPermissionName}\n variant=\"compact\"\n />\n )}\n {canViewMetricsForEnv && metricsError && renderError(metricsError)}\n {canViewMetricsForEnv && (\n <MetricsActions\n onRefresh={handleRefresh}\n disabled={metricsLoading}\n />\n )}\n {canViewMetricsForEnv && (\n <Grid\n container\n spacing={4}\n className={classes.metricsGridContainer}\n >\n <Grid item xs={12} md={6}>\n <Card>\n <CardHeader title=\"CPU Usage\" />\n <Divider />\n <CardContent>\n <MetricGraphByComponent\n usageData={\n resourceMetrics?.cpuUsage || ({} as CpuUsageMetrics)\n }\n usageType=\"cpu\"\n timeRange={filters.timeRange}\n customStartTime={filters.customStartTime}\n customEndTime={filters.customEndTime}\n />\n </CardContent>\n </Card>\n </Grid>\n <Grid item xs={12} md={6}>\n <Card>\n <CardHeader title=\"Memory Usage\" />\n <Divider />\n <CardContent>\n <MetricGraphByComponent\n usageData={\n resourceMetrics?.memoryUsage ||\n ({} as MemoryUsageMetrics)\n }\n usageType=\"memory\"\n timeRange={filters.timeRange}\n customStartTime={filters.customStartTime}\n customEndTime={filters.customEndTime}\n />\n </CardContent>\n </Card>\n </Grid>\n <HTTPMetricsSection\n filters={filters}\n entity={entity}\n namespaceName={namespace as string}\n project={project as string}\n />\n </Grid>\n )}\n </>\n )}\n </Box>\n );\n};\n\nexport const ObservabilityMetricsPage = () => {\n const {\n canViewMetrics,\n loading: permissionLoading,\n deniedTooltip,\n permissionName,\n } = useMetricsPermission();\n\n if (permissionLoading) {\n return <Progress />;\n }\n\n if (!canViewMetrics) {\n return (\n <ForbiddenState\n message={deniedTooltip}\n permissionName={permissionName}\n variant=\"fullpage\"\n />\n );\n }\n\n return <ObservabilityMetricsContent />;\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { makeStyles } from '@material-ui/core/styles';
|
|
2
|
+
|
|
3
|
+
const useMetricsActionsStyles = makeStyles((theme) => ({
|
|
4
|
+
statsContainer: {
|
|
5
|
+
marginTop: theme.spacing(2),
|
|
6
|
+
display: "flex",
|
|
7
|
+
justifyContent: "space-between",
|
|
8
|
+
alignItems: "center"
|
|
9
|
+
},
|
|
10
|
+
actionsContainer: {
|
|
11
|
+
display: "flex",
|
|
12
|
+
gap: theme.spacing(1),
|
|
13
|
+
alignItems: "center"
|
|
14
|
+
}
|
|
15
|
+
}));
|
|
16
|
+
const useMetricGraphStyles = makeStyles((theme) => ({
|
|
17
|
+
chartContainer: {
|
|
18
|
+
width: "100%",
|
|
19
|
+
position: "relative"
|
|
20
|
+
},
|
|
21
|
+
lineChart: {
|
|
22
|
+
width: "100%",
|
|
23
|
+
maxWidth: "100%",
|
|
24
|
+
maxHeight: "70vh",
|
|
25
|
+
aspectRatio: 1.618
|
|
26
|
+
},
|
|
27
|
+
emptyOverlay: {
|
|
28
|
+
alignItems: "center",
|
|
29
|
+
color: theme.palette.text.secondary,
|
|
30
|
+
display: "flex",
|
|
31
|
+
fontSize: "1rem",
|
|
32
|
+
justifyContent: "center",
|
|
33
|
+
left: 0,
|
|
34
|
+
pointerEvents: "none",
|
|
35
|
+
position: "absolute",
|
|
36
|
+
right: 0,
|
|
37
|
+
top: 0,
|
|
38
|
+
bottom: 0
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
41
|
+
const useObservabilityMetricsPageStyles = makeStyles((theme) => ({
|
|
42
|
+
metricsGridContainer: {
|
|
43
|
+
marginTop: theme.spacing(0)
|
|
44
|
+
},
|
|
45
|
+
errorContainer: {
|
|
46
|
+
marginTop: theme.spacing(2),
|
|
47
|
+
marginBottom: theme.spacing(2)
|
|
48
|
+
},
|
|
49
|
+
metricsContentContainer: {
|
|
50
|
+
padding: theme.spacing(0)
|
|
51
|
+
}
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
export { useMetricGraphStyles, useMetricsActionsStyles, useObservabilityMetricsPageStyles };
|
|
55
|
+
//# sourceMappingURL=styles.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.esm.js","sources":["../../../src/components/Metrics/styles.ts"],"sourcesContent":["import { makeStyles, Theme } from '@material-ui/core/styles';\n\nexport const useMetricsActionsStyles = makeStyles((theme: Theme) => ({\n statsContainer: {\n marginTop: theme.spacing(2),\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n },\n actionsContainer: {\n display: 'flex',\n gap: theme.spacing(1),\n alignItems: 'center',\n },\n}));\n\nexport const useMetricGraphStyles = makeStyles((theme: Theme) => ({\n chartContainer: {\n width: '100%',\n position: 'relative',\n },\n lineChart: {\n width: '100%',\n maxWidth: '100%',\n maxHeight: '70vh',\n aspectRatio: 1.618,\n },\n emptyOverlay: {\n alignItems: 'center',\n color: theme.palette.text.secondary,\n display: 'flex',\n fontSize: '1rem',\n justifyContent: 'center',\n left: 0,\n pointerEvents: 'none',\n position: 'absolute',\n right: 0,\n top: 0,\n bottom: 0,\n },\n}));\n\nexport const useObservabilityMetricsPageStyles = makeStyles((theme: Theme) => ({\n metricsGridContainer: {\n marginTop: theme.spacing(0),\n },\n errorContainer: {\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n },\n metricsContentContainer: {\n padding: theme.spacing(0),\n },\n}));\n"],"names":[],"mappings":";;AAEO,MAAM,uBAAA,GAA0B,UAAA,CAAW,CAAC,KAAA,MAAkB;AAAA,EACnE,cAAA,EAAgB;AAAA,IACd,SAAA,EAAW,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,IAC1B,OAAA,EAAS,MAAA;AAAA,IACT,cAAA,EAAgB,eAAA;AAAA,IAChB,UAAA,EAAY;AAAA,GACd;AAAA,EACA,gBAAA,EAAkB;AAAA,IAChB,OAAA,EAAS,MAAA;AAAA,IACT,GAAA,EAAK,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,IACpB,UAAA,EAAY;AAAA;AAEhB,CAAA,CAAE;AAEK,MAAM,oBAAA,GAAuB,UAAA,CAAW,CAAC,KAAA,MAAkB;AAAA,EAChE,cAAA,EAAgB;AAAA,IACd,KAAA,EAAO,MAAA;AAAA,IACP,QAAA,EAAU;AAAA,GACZ;AAAA,EACA,SAAA,EAAW;AAAA,IACT,KAAA,EAAO,MAAA;AAAA,IACP,QAAA,EAAU,MAAA;AAAA,IACV,SAAA,EAAW,MAAA;AAAA,IACX,WAAA,EAAa;AAAA,GACf;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,UAAA,EAAY,QAAA;AAAA,IACZ,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,SAAA;AAAA,IAC1B,OAAA,EAAS,MAAA;AAAA,IACT,QAAA,EAAU,MAAA;AAAA,IACV,cAAA,EAAgB,QAAA;AAAA,IAChB,IAAA,EAAM,CAAA;AAAA,IACN,aAAA,EAAe,MAAA;AAAA,IACf,QAAA,EAAU,UAAA;AAAA,IACV,KAAA,EAAO,CAAA;AAAA,IACP,GAAA,EAAK,CAAA;AAAA,IACL,MAAA,EAAQ;AAAA;AAEZ,CAAA,CAAE;AAEK,MAAM,iCAAA,GAAoC,UAAA,CAAW,CAAC,KAAA,MAAkB;AAAA,EAC7E,oBAAA,EAAsB;AAAA,IACpB,SAAA,EAAW,KAAA,CAAM,OAAA,CAAQ,CAAC;AAAA,GAC5B;AAAA,EACA,cAAA,EAAgB;AAAA,IACd,SAAA,EAAW,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,IAC1B,YAAA,EAAc,KAAA,CAAM,OAAA,CAAQ,CAAC;AAAA,GAC/B;AAAA,EACA,uBAAA,EAAyB;AAAA,IACvB,OAAA,EAAS,KAAA,CAAM,OAAA,CAAQ,CAAC;AAAA;AAE5B,CAAA,CAAE;;;;"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
const formatAxisTime = (timestamp, daysRange) => {
|
|
2
|
+
const date = new Date(timestamp);
|
|
3
|
+
if (daysRange > 1) {
|
|
4
|
+
const year = date.getFullYear();
|
|
5
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
6
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
7
|
+
return `${year}/${month}/${day}`;
|
|
8
|
+
}
|
|
9
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
10
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
11
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
12
|
+
return `${hours}:${minutes}:${seconds}`;
|
|
13
|
+
};
|
|
14
|
+
const formatTooltipTime = (timestamp) => {
|
|
15
|
+
const date = new Date(timestamp);
|
|
16
|
+
return date.toLocaleString("en-US", {
|
|
17
|
+
month: "short",
|
|
18
|
+
day: "numeric",
|
|
19
|
+
year: "numeric",
|
|
20
|
+
hour: "2-digit",
|
|
21
|
+
minute: "2-digit",
|
|
22
|
+
second: "2-digit"
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const formatMetricValue = (value, usageType) => {
|
|
26
|
+
if (value === 0) return "0";
|
|
27
|
+
if (usageType === "cpu") {
|
|
28
|
+
if (value > 1) return `${value.toFixed(4)}`;
|
|
29
|
+
if (value > 1e-3) return `${(value * 1e3).toFixed(2)} mCPU`;
|
|
30
|
+
return `${(value * 1e6).toFixed(2)} uCPU`;
|
|
31
|
+
}
|
|
32
|
+
if (usageType === "memory") {
|
|
33
|
+
if (value > 1e6) return `${(value / 1e6).toFixed(2)} MB`;
|
|
34
|
+
if (value > 1e3) return `${(value / 1e3).toFixed(2)} KB`;
|
|
35
|
+
return `${value.toFixed(2)} B`;
|
|
36
|
+
}
|
|
37
|
+
if (usageType === "networkThroughput") {
|
|
38
|
+
return `${value.toFixed(2)} req/s`;
|
|
39
|
+
}
|
|
40
|
+
if (usageType === "networkLatency") {
|
|
41
|
+
if (value > 1) return `${value.toFixed(2)} s`;
|
|
42
|
+
if (value > 1e-3) return `${(value * 1e3).toFixed(2)} ms`;
|
|
43
|
+
return `${(value * 1e6).toFixed(2)} us`;
|
|
44
|
+
}
|
|
45
|
+
return value.toFixed(2);
|
|
46
|
+
};
|
|
47
|
+
const parseTimeRange = (range) => {
|
|
48
|
+
if (!range) return null;
|
|
49
|
+
const value = parseInt(range.slice(0, -1), 10);
|
|
50
|
+
const unit = range.slice(-1);
|
|
51
|
+
switch (unit) {
|
|
52
|
+
case "m":
|
|
53
|
+
return value * 60 * 1e3;
|
|
54
|
+
case "h":
|
|
55
|
+
return value * 60 * 60 * 1e3;
|
|
56
|
+
case "d":
|
|
57
|
+
return value * 24 * 60 * 60 * 1e3;
|
|
58
|
+
default:
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const calculateTimeDomain = (transformedData, timeRange, tickCount = 5, customRange) => {
|
|
63
|
+
let minTimestamp;
|
|
64
|
+
let maxTimestamp;
|
|
65
|
+
const customStartMs = customRange?.startTime ? new Date(customRange.startTime).getTime() : NaN;
|
|
66
|
+
const customEndMs = customRange?.endTime ? new Date(customRange.endTime).getTime() : NaN;
|
|
67
|
+
const hasCustomRange = timeRange === "custom" && Number.isFinite(customStartMs) && Number.isFinite(customEndMs) && customEndMs > customStartMs;
|
|
68
|
+
const duration = parseTimeRange(timeRange);
|
|
69
|
+
if (hasCustomRange) {
|
|
70
|
+
minTimestamp = customStartMs;
|
|
71
|
+
maxTimestamp = customEndMs;
|
|
72
|
+
} else if (duration) {
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
minTimestamp = now - duration;
|
|
75
|
+
maxTimestamp = now;
|
|
76
|
+
} else if (transformedData.length > 0) {
|
|
77
|
+
minTimestamp = transformedData[0].timestamp;
|
|
78
|
+
maxTimestamp = transformedData[transformedData.length - 1].timestamp;
|
|
79
|
+
} else {
|
|
80
|
+
return { ticks: [], daysRange: 0, domain: [0, 1] };
|
|
81
|
+
}
|
|
82
|
+
const timeRangeDuration = maxTimestamp - minTimestamp;
|
|
83
|
+
const daysRange = timeRangeDuration / (1e3 * 60 * 60 * 24);
|
|
84
|
+
const tickInterval = timeRangeDuration / (tickCount - 1);
|
|
85
|
+
const ticks = Array.from(
|
|
86
|
+
{ length: tickCount },
|
|
87
|
+
(_, i) => minTimestamp + i * tickInterval
|
|
88
|
+
);
|
|
89
|
+
return {
|
|
90
|
+
ticks,
|
|
91
|
+
daysRange,
|
|
92
|
+
domain: [minTimestamp, maxTimestamp]
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
const injectPerMetricGaps = (usageData) => {
|
|
96
|
+
const result = {};
|
|
97
|
+
Object.entries(usageData).forEach(([metricName, points]) => {
|
|
98
|
+
const sortedPoints = [...points].sort((a, b) => {
|
|
99
|
+
const aEpoch = new Date(a.timestamp).getTime();
|
|
100
|
+
const bEpoch = new Date(b.timestamp).getTime();
|
|
101
|
+
return aEpoch - bEpoch;
|
|
102
|
+
});
|
|
103
|
+
if (sortedPoints.length < 2) {
|
|
104
|
+
result[metricName] = sortedPoints;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const epochs = sortedPoints.map((p) => new Date(p.timestamp).getTime());
|
|
108
|
+
const intervals = epochs.slice(1).map((t, i) => t - epochs[i]);
|
|
109
|
+
const sortedIntervals = [...intervals].sort((a, b) => a - b);
|
|
110
|
+
const medianInterval = sortedIntervals[Math.floor(sortedIntervals.length / 2)];
|
|
111
|
+
const gapThreshold = medianInterval * 2;
|
|
112
|
+
const injected = [];
|
|
113
|
+
for (let i = 0; i < sortedPoints.length; i++) {
|
|
114
|
+
injected.push(sortedPoints[i]);
|
|
115
|
+
if (i < sortedPoints.length - 1 && epochs[i + 1] - epochs[i] > gapThreshold) {
|
|
116
|
+
const sentinelEpoch = epochs[i] + medianInterval;
|
|
117
|
+
injected.push({ timestamp: String(sentinelEpoch), value: null });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
result[metricName] = injected;
|
|
121
|
+
});
|
|
122
|
+
return result;
|
|
123
|
+
};
|
|
124
|
+
const transformMetricsData = (usageData) => {
|
|
125
|
+
const gapped = injectPerMetricGaps(usageData);
|
|
126
|
+
const timeMap = /* @__PURE__ */ new Map();
|
|
127
|
+
Object.entries(gapped).forEach(([metricName, metricData]) => {
|
|
128
|
+
metricData.forEach((point) => {
|
|
129
|
+
if (!timeMap.has(point.timestamp)) {
|
|
130
|
+
const epochMs = isNaN(Number(point.timestamp)) ? new Date(point.timestamp).getTime() : Number(point.timestamp);
|
|
131
|
+
timeMap.set(point.timestamp, {
|
|
132
|
+
time: point.timestamp,
|
|
133
|
+
timestamp: epochMs
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
timeMap.get(point.timestamp)[metricName] = point.value;
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
return Array.from(timeMap.values()).sort((a, b) => a.timestamp - b.timestamp);
|
|
140
|
+
};
|
|
141
|
+
const getMetricConfigs = (usageType) => {
|
|
142
|
+
if (usageType === "networkThroughput") {
|
|
143
|
+
return {
|
|
144
|
+
totalRequests: {
|
|
145
|
+
key: "requestCount",
|
|
146
|
+
color: "#8884d8"
|
|
147
|
+
},
|
|
148
|
+
successfulRequests: {
|
|
149
|
+
key: "successfulRequestCount",
|
|
150
|
+
color: "#82ca9d"
|
|
151
|
+
},
|
|
152
|
+
unsuccessfulRequests: {
|
|
153
|
+
key: "unsuccessfulRequestCount",
|
|
154
|
+
color: "#ff7f7f"
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (usageType === "networkLatency") {
|
|
159
|
+
return {
|
|
160
|
+
meanLatency: {
|
|
161
|
+
key: "meanLatency",
|
|
162
|
+
color: "#8884d8"
|
|
163
|
+
},
|
|
164
|
+
p50Latency: {
|
|
165
|
+
key: "latencyP50",
|
|
166
|
+
color: "#82ca9d"
|
|
167
|
+
},
|
|
168
|
+
p90Latency: {
|
|
169
|
+
key: "latencyP90",
|
|
170
|
+
color: "#ffc658"
|
|
171
|
+
},
|
|
172
|
+
p99Latency: {
|
|
173
|
+
key: "latencyP99",
|
|
174
|
+
color: "#ff7300"
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (usageType === "cpu") {
|
|
179
|
+
return {
|
|
180
|
+
usage: {
|
|
181
|
+
key: "cpuUsage",
|
|
182
|
+
color: "#8884d8"
|
|
183
|
+
},
|
|
184
|
+
requests: {
|
|
185
|
+
key: "cpuRequests",
|
|
186
|
+
color: "#82ca9d"
|
|
187
|
+
},
|
|
188
|
+
limits: {
|
|
189
|
+
key: "cpuLimits",
|
|
190
|
+
color: "#ffc658"
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
usage: {
|
|
196
|
+
key: "memoryUsage",
|
|
197
|
+
color: "#8884d8"
|
|
198
|
+
},
|
|
199
|
+
requests: {
|
|
200
|
+
key: "memoryRequests",
|
|
201
|
+
color: "#82ca9d"
|
|
202
|
+
},
|
|
203
|
+
limits: {
|
|
204
|
+
key: "memoryLimits",
|
|
205
|
+
color: "#ffc658"
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
const getLineOpacity = (metricKey, hoveringDataKey) => {
|
|
210
|
+
return !hoveringDataKey || hoveringDataKey === metricKey ? 1 : 0.5;
|
|
211
|
+
};
|
|
212
|
+
const formatMetricName = (key) => {
|
|
213
|
+
const name = key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
|
|
214
|
+
if (name.includes("Cpu")) {
|
|
215
|
+
return name.replace("Cpu", "CPU");
|
|
216
|
+
}
|
|
217
|
+
return name;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export { calculateTimeDomain, formatAxisTime, formatMetricName, formatMetricValue, formatTooltipTime, getLineOpacity, getMetricConfigs, parseTimeRange, transformMetricsData };
|
|
221
|
+
//# sourceMappingURL=utils.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.esm.js","sources":["../../../src/components/Metrics/utils.ts"],"sourcesContent":["import { DataKey } from 'recharts/types/util/types';\nimport {\n CpuUsageMetrics,\n MemoryUsageMetrics,\n NetworkLatencyMetrics,\n NetworkThroughputMetrics,\n} from '../../types';\n\n/**\n * Format timestamp for axis display based on the time range\n */\nexport const formatAxisTime = (\n timestamp: number,\n daysRange: number,\n): string => {\n const date = new Date(timestamp);\n\n if (daysRange > 1) {\n // For ranges > 1 day, show date in yyyy/mm/dd format\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const day = String(date.getDate()).padStart(2, '0');\n return `${year}/${month}/${day}`;\n }\n\n // For <= 1 day, show time in HH:MM:SS format\n const hours = String(date.getHours()).padStart(2, '0');\n const minutes = String(date.getMinutes()).padStart(2, '0');\n const seconds = String(date.getSeconds()).padStart(2, '0');\n return `${hours}:${minutes}:${seconds}`;\n};\n\n/**\n * Format tooltip label with full date and time\n */\nexport const formatTooltipTime = (timestamp: number): string => {\n const date = new Date(timestamp);\n return date.toLocaleString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n });\n};\n\n/**\n * Format metric value based on usage type\n */\nexport const formatMetricValue = (\n value: number,\n usageType: 'cpu' | 'memory' | 'networkThroughput' | 'networkLatency',\n): string => {\n if (value === 0) return '0';\n\n if (usageType === 'cpu') {\n // value is in CPU cores\n if (value > 1) return `${value.toFixed(4)}`;\n if (value > 0.001) return `${(value * 1000).toFixed(2)} mCPU`;\n return `${(value * 1000000).toFixed(2)} uCPU`;\n }\n if (usageType === 'memory') {\n // value is in Bytes\n if (value > 1000000) return `${(value / 1000000).toFixed(2)} MB`;\n if (value > 1000) return `${(value / 1000).toFixed(2)} KB`;\n return `${value.toFixed(2)} B`;\n }\n if (usageType === 'networkThroughput') {\n return `${value.toFixed(2)} req/s`;\n }\n if (usageType === 'networkLatency') {\n // value is in seconds\n if (value > 1) return `${value.toFixed(2)} s`;\n if (value > 0.001) return `${(value * 1000).toFixed(2)} ms`;\n return `${(value * 1000000).toFixed(2)} us`;\n }\n return value.toFixed(2);\n};\n\n/**\n * Parse time range string (e.g., \"14d\", \"1h\", \"30m\") to milliseconds\n */\nexport const parseTimeRange = (range?: string): number | null => {\n if (!range) return null;\n\n const value = parseInt(range.slice(0, -1), 10);\n const unit = range.slice(-1);\n\n switch (unit) {\n case 'm':\n return value * 60 * 1000;\n case 'h':\n return value * 60 * 60 * 1000;\n case 'd':\n return value * 24 * 60 * 60 * 1000;\n default:\n return null;\n }\n};\n\n/**\n * Calculate time domain and ticks for the chart.\n * If `customRange` is provided (used for `timeRange === 'custom'`), it takes\n * precedence over the preset duration so the axis matches the user's window.\n */\nexport const calculateTimeDomain = (\n transformedData: Array<{ timestamp: number }>,\n timeRange?: string,\n tickCount: number = 5,\n customRange?: { startTime?: string; endTime?: string },\n) => {\n let minTimestamp: number;\n let maxTimestamp: number;\n\n const customStartMs = customRange?.startTime\n ? new Date(customRange.startTime).getTime()\n : NaN;\n const customEndMs = customRange?.endTime\n ? new Date(customRange.endTime).getTime()\n : NaN;\n const hasCustomRange =\n timeRange === 'custom' &&\n Number.isFinite(customStartMs) &&\n Number.isFinite(customEndMs) &&\n customEndMs > customStartMs;\n\n const duration = parseTimeRange(timeRange);\n\n if (hasCustomRange) {\n minTimestamp = customStartMs;\n maxTimestamp = customEndMs;\n } else if (duration) {\n // Use selected range\n const now = Date.now();\n minTimestamp = now - duration;\n maxTimestamp = now;\n } else if (transformedData.length > 0) {\n // Fall back to data range\n minTimestamp = transformedData[0].timestamp;\n maxTimestamp = transformedData[transformedData.length - 1].timestamp;\n } else {\n return { ticks: [], daysRange: 0, domain: [0, 1] as [number, number] };\n }\n\n const timeRangeDuration = maxTimestamp - minTimestamp;\n const daysRange = timeRangeDuration / (1000 * 60 * 60 * 24);\n const tickInterval = timeRangeDuration / (tickCount - 1);\n\n const ticks = Array.from(\n { length: tickCount },\n (_, i) => minTimestamp + i * tickInterval,\n );\n\n return {\n ticks,\n daysRange,\n domain: [minTimestamp, maxTimestamp] as [number, number],\n };\n};\n\ntype RawPoint = { timestamp: string; value: number | null };\ntype MetricDataPoint = { timestamp: number; [key: string]: any };\n\nconst injectPerMetricGaps = (\n usageData: Record<string, RawPoint[]>,\n): Record<string, RawPoint[]> => {\n const result: Record<string, RawPoint[]> = {};\n\n Object.entries(usageData).forEach(([metricName, points]) => {\n const sortedPoints = [...points].sort((a, b) => {\n const aEpoch = new Date(a.timestamp).getTime();\n const bEpoch = new Date(b.timestamp).getTime();\n return aEpoch - bEpoch;\n });\n\n if (sortedPoints.length < 2) {\n result[metricName] = sortedPoints;\n return;\n }\n\n const epochs = sortedPoints.map(p => new Date(p.timestamp).getTime());\n const intervals = epochs.slice(1).map((t, i) => t - epochs[i]);\n const sortedIntervals = [...intervals].sort((a, b) => a - b);\n const medianInterval =\n sortedIntervals[Math.floor(sortedIntervals.length / 2)];\n const gapThreshold = medianInterval * 2;\n\n const injected: RawPoint[] = [];\n for (let i = 0; i < sortedPoints.length; i++) {\n injected.push(sortedPoints[i]);\n if (\n i < sortedPoints.length - 1 &&\n epochs[i + 1] - epochs[i] > gapThreshold\n ) {\n // Sentinel key: numeric ms string, distinct from ISO timestamp keys\n const sentinelEpoch = epochs[i] + medianInterval;\n injected.push({ timestamp: String(sentinelEpoch), value: null });\n }\n }\n result[metricName] = injected;\n });\n\n return result;\n};\n\n/**\n * Transform metrics data structure to be compatible with Recharts\n */\nexport const transformMetricsData = (\n usageData:\n | CpuUsageMetrics\n | MemoryUsageMetrics\n | NetworkThroughputMetrics\n | NetworkLatencyMetrics,\n) => {\n const gapped = injectPerMetricGaps(usageData as Record<string, RawPoint[]>);\n const timeMap = new Map<string, MetricDataPoint>();\n\n // Collect all unique timestamps and merge metric values\n Object.entries(gapped).forEach(([metricName, metricData]) => {\n metricData.forEach(point => {\n if (!timeMap.has(point.timestamp)) {\n const epochMs = isNaN(Number(point.timestamp))\n ? new Date(point.timestamp).getTime()\n : Number(point.timestamp);\n timeMap.set(point.timestamp, {\n time: point.timestamp,\n timestamp: epochMs,\n });\n }\n timeMap.get(point.timestamp)![metricName] = point.value;\n });\n });\n\n return Array.from(timeMap.values()).sort((a, b) => a.timestamp - b.timestamp);\n};\n\n/**\n * Metric configuration for CPU and Memory charts\n */\nexport interface MetricConfig {\n key: string;\n color: string;\n}\n\nexport const getMetricConfigs = (\n usageType: 'cpu' | 'memory' | 'networkThroughput' | 'networkLatency',\n): Record<string, MetricConfig> => {\n if (usageType === 'networkThroughput') {\n return {\n totalRequests: {\n key: 'requestCount',\n color: '#8884d8',\n },\n successfulRequests: {\n key: 'successfulRequestCount',\n color: '#82ca9d',\n },\n unsuccessfulRequests: {\n key: 'unsuccessfulRequestCount',\n color: '#ff7f7f',\n },\n };\n }\n\n if (usageType === 'networkLatency') {\n return {\n meanLatency: {\n key: 'meanLatency',\n color: '#8884d8',\n },\n p50Latency: {\n key: 'latencyP50',\n color: '#82ca9d',\n },\n p90Latency: {\n key: 'latencyP90',\n color: '#ffc658',\n },\n p99Latency: {\n key: 'latencyP99',\n color: '#ff7300',\n },\n };\n }\n\n if (usageType === 'cpu') {\n return {\n usage: {\n key: 'cpuUsage',\n color: '#8884d8',\n },\n requests: {\n key: 'cpuRequests',\n color: '#82ca9d',\n },\n limits: {\n key: 'cpuLimits',\n color: '#ffc658',\n },\n };\n }\n\n return {\n usage: {\n key: 'memoryUsage',\n color: '#8884d8',\n },\n requests: {\n key: 'memoryRequests',\n color: '#82ca9d',\n },\n limits: {\n key: 'memoryLimits',\n color: '#ffc658',\n },\n };\n};\n\n/**\n * Calculate opacity for metric lines based on hover state\n */\nexport const getLineOpacity = (\n metricKey: string,\n hoveringDataKey?: DataKey<any>,\n): number => {\n return !hoveringDataKey || hoveringDataKey === metricKey ? 1 : 0.5;\n};\n\n/**\n * Format metric key to display name (e.g., 'cpuUsage' -> 'CPU Usage')\n */\nexport const formatMetricName = (key: string): string => {\n const name = key\n .replace(/([A-Z])/g, ' $1')\n .replace(/^./, str => str.toUpperCase());\n if (name.includes('Cpu')) {\n // Capitalize to CPU\n return name.replace('Cpu', 'CPU');\n }\n return name;\n};\n"],"names":[],"mappings":"AAWO,MAAM,cAAA,GAAiB,CAC5B,SAAA,EACA,SAAA,KACW;AACX,EAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,SAAS,CAAA;AAE/B,EAAA,IAAI,YAAY,CAAA,EAAG;AAEjB,IAAA,MAAM,IAAA,GAAO,KAAK,WAAA,EAAY;AAC9B,IAAA,MAAM,KAAA,GAAQ,OAAO,IAAA,CAAK,QAAA,KAAa,CAAC,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAA;AACzD,IAAA,MAAM,GAAA,GAAM,OAAO,IAAA,CAAK,OAAA,EAAS,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AAClD,IAAA,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,KAAK,IAAI,GAAG,CAAA,CAAA;AAAA,EAChC;AAGA,EAAA,MAAM,KAAA,GAAQ,OAAO,IAAA,CAAK,QAAA,EAAU,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AACrD,EAAA,MAAM,OAAA,GAAU,OAAO,IAAA,CAAK,UAAA,EAAY,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AACzD,EAAA,MAAM,OAAA,GAAU,OAAO,IAAA,CAAK,UAAA,EAAY,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AACzD,EAAA,OAAO,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,OAAO,IAAI,OAAO,CAAA,CAAA;AACvC;AAKO,MAAM,iBAAA,GAAoB,CAAC,SAAA,KAA8B;AAC9D,EAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,SAAS,CAAA;AAC/B,EAAA,OAAO,IAAA,CAAK,eAAe,OAAA,EAAS;AAAA,IAClC,KAAA,EAAO,OAAA;AAAA,IACP,GAAA,EAAK,SAAA;AAAA,IACL,IAAA,EAAM,SAAA;AAAA,IACN,IAAA,EAAM,SAAA;AAAA,IACN,MAAA,EAAQ,SAAA;AAAA,IACR,MAAA,EAAQ;AAAA,GACT,CAAA;AACH;AAKO,MAAM,iBAAA,GAAoB,CAC/B,KAAA,EACA,SAAA,KACW;AACX,EAAA,IAAI,KAAA,KAAU,GAAG,OAAO,GAAA;AAExB,EAAA,IAAI,cAAc,KAAA,EAAO;AAEvB,IAAA,IAAI,QAAQ,CAAA,EAAG,OAAO,GAAG,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA;AACzC,IAAA,IAAI,KAAA,GAAQ,MAAO,OAAO,CAAA,EAAA,CAAI,QAAQ,GAAA,EAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,KAAA,CAAA;AACtD,IAAA,OAAO,CAAA,EAAA,CAAI,KAAA,GAAQ,GAAA,EAAS,OAAA,CAAQ,CAAC,CAAC,CAAA,KAAA,CAAA;AAAA,EACxC;AACA,EAAA,IAAI,cAAc,QAAA,EAAU;AAE1B,IAAA,IAAI,KAAA,GAAQ,KAAS,OAAO,CAAA,EAAA,CAAI,QAAQ,GAAA,EAAS,OAAA,CAAQ,CAAC,CAAC,CAAA,GAAA,CAAA;AAC3D,IAAA,IAAI,KAAA,GAAQ,KAAM,OAAO,CAAA,EAAA,CAAI,QAAQ,GAAA,EAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,GAAA,CAAA;AACrD,IAAA,OAAO,CAAA,EAAG,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAA,CAAA;AAAA,EAC5B;AACA,EAAA,IAAI,cAAc,mBAAA,EAAqB;AACrC,IAAA,OAAO,CAAA,EAAG,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,MAAA,CAAA;AAAA,EAC5B;AACA,EAAA,IAAI,cAAc,gBAAA,EAAkB;AAElC,IAAA,IAAI,QAAQ,CAAA,EAAG,OAAO,GAAG,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAA,CAAA;AACzC,IAAA,IAAI,KAAA,GAAQ,MAAO,OAAO,CAAA,EAAA,CAAI,QAAQ,GAAA,EAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,GAAA,CAAA;AACtD,IAAA,OAAO,CAAA,EAAA,CAAI,KAAA,GAAQ,GAAA,EAAS,OAAA,CAAQ,CAAC,CAAC,CAAA,GAAA,CAAA;AAAA,EACxC;AACA,EAAA,OAAO,KAAA,CAAM,QAAQ,CAAC,CAAA;AACxB;AAKO,MAAM,cAAA,GAAiB,CAAC,KAAA,KAAkC;AAC/D,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AAEnB,EAAA,MAAM,QAAQ,QAAA,CAAS,KAAA,CAAM,MAAM,CAAA,EAAG,EAAE,GAAG,EAAE,CAAA;AAC7C,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,EAAE,CAAA;AAE3B,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,GAAA;AACH,MAAA,OAAO,QAAQ,EAAA,GAAK,GAAA;AAAA,IACtB,KAAK,GAAA;AACH,MAAA,OAAO,KAAA,GAAQ,KAAK,EAAA,GAAK,GAAA;AAAA,IAC3B,KAAK,GAAA;AACH,MAAA,OAAO,KAAA,GAAQ,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAAA,IAChC;AACE,MAAA,OAAO,IAAA;AAAA;AAEb;AAOO,MAAM,sBAAsB,CACjC,eAAA,EACA,SAAA,EACA,SAAA,GAAoB,GACpB,WAAA,KACG;AACH,EAAA,IAAI,YAAA;AACJ,EAAA,IAAI,YAAA;AAEJ,EAAA,MAAM,aAAA,GAAgB,aAAa,SAAA,GAC/B,IAAI,KAAK,WAAA,CAAY,SAAS,CAAA,CAAE,OAAA,EAAQ,GACxC,GAAA;AACJ,EAAA,MAAM,WAAA,GAAc,aAAa,OAAA,GAC7B,IAAI,KAAK,WAAA,CAAY,OAAO,CAAA,CAAE,OAAA,EAAQ,GACtC,GAAA;AACJ,EAAA,MAAM,cAAA,GACJ,SAAA,KAAc,QAAA,IACd,MAAA,CAAO,QAAA,CAAS,aAAa,CAAA,IAC7B,MAAA,CAAO,QAAA,CAAS,WAAW,CAAA,IAC3B,WAAA,GAAc,aAAA;AAEhB,EAAA,MAAM,QAAA,GAAW,eAAe,SAAS,CAAA;AAEzC,EAAA,IAAI,cAAA,EAAgB;AAClB,IAAA,YAAA,GAAe,aAAA;AACf,IAAA,YAAA,GAAe,WAAA;AAAA,EACjB,WAAW,QAAA,EAAU;AAEnB,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,YAAA,GAAe,GAAA,GAAM,QAAA;AACrB,IAAA,YAAA,GAAe,GAAA;AAAA,EACjB,CAAA,MAAA,IAAW,eAAA,CAAgB,MAAA,GAAS,CAAA,EAAG;AAErC,IAAA,YAAA,GAAe,eAAA,CAAgB,CAAC,CAAA,CAAE,SAAA;AAClC,IAAA,YAAA,GAAe,eAAA,CAAgB,eAAA,CAAgB,MAAA,GAAS,CAAC,CAAA,CAAE,SAAA;AAAA,EAC7D,CAAA,MAAO;AACL,IAAA,OAAO,EAAE,KAAA,EAAO,EAAC,EAAG,SAAA,EAAW,GAAG,MAAA,EAAQ,CAAC,CAAA,EAAG,CAAC,CAAA,EAAsB;AAAA,EACvE;AAEA,EAAA,MAAM,oBAAoB,YAAA,GAAe,YAAA;AACzC,EAAA,MAAM,SAAA,GAAY,iBAAA,IAAqB,GAAA,GAAO,EAAA,GAAK,EAAA,GAAK,EAAA,CAAA;AACxD,EAAA,MAAM,YAAA,GAAe,qBAAqB,SAAA,GAAY,CAAA,CAAA;AAEtD,EAAA,MAAM,QAAQ,KAAA,CAAM,IAAA;AAAA,IAClB,EAAE,QAAQ,SAAA,EAAU;AAAA,IACpB,CAAC,CAAA,EAAG,CAAA,KAAM,YAAA,GAAe,CAAA,GAAI;AAAA,GAC/B;AAEA,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA,EAAQ,CAAC,YAAA,EAAc,YAAY;AAAA,GACrC;AACF;AAKA,MAAM,mBAAA,GAAsB,CAC1B,SAAA,KAC+B;AAC/B,EAAA,MAAM,SAAqC,EAAC;AAE5C,EAAA,MAAA,CAAO,OAAA,CAAQ,SAAS,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,UAAA,EAAY,MAAM,CAAA,KAAM;AAC1D,IAAA,MAAM,YAAA,GAAe,CAAC,GAAG,MAAM,EAAE,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM;AAC9C,MAAA,MAAM,SAAS,IAAI,IAAA,CAAK,CAAA,CAAE,SAAS,EAAE,OAAA,EAAQ;AAC7C,MAAA,MAAM,SAAS,IAAI,IAAA,CAAK,CAAA,CAAE,SAAS,EAAE,OAAA,EAAQ;AAC7C,MAAA,OAAO,MAAA,GAAS,MAAA;AAAA,IAClB,CAAC,CAAA;AAED,IAAA,IAAI,YAAA,CAAa,SAAS,CAAA,EAAG;AAC3B,MAAA,MAAA,CAAO,UAAU,CAAA,GAAI,YAAA;AACrB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,MAAA,GAAS,YAAA,CAAa,GAAA,CAAI,CAAA,CAAA,KAAK,IAAI,KAAK,CAAA,CAAE,SAAS,CAAA,CAAE,OAAA,EAAS,CAAA;AACpE,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,KAAA,CAAM,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,MAAA,CAAO,CAAC,CAAC,CAAA;AAC7D,IAAA,MAAM,eAAA,GAAkB,CAAC,GAAG,SAAS,CAAA,CAAE,KAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AAC3D,IAAA,MAAM,iBACJ,eAAA,CAAgB,IAAA,CAAK,MAAM,eAAA,CAAgB,MAAA,GAAS,CAAC,CAAC,CAAA;AACxD,IAAA,MAAM,eAAe,cAAA,GAAiB,CAAA;AAEtC,IAAA,MAAM,WAAuB,EAAC;AAC9B,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,YAAA,CAAa,QAAQ,CAAA,EAAA,EAAK;AAC5C,MAAA,QAAA,CAAS,IAAA,CAAK,YAAA,CAAa,CAAC,CAAC,CAAA;AAC7B,MAAA,IACE,CAAA,GAAI,YAAA,CAAa,MAAA,GAAS,CAAA,IAC1B,MAAA,CAAO,CAAA,GAAI,CAAC,CAAA,GAAI,MAAA,CAAO,CAAC,CAAA,GAAI,YAAA,EAC5B;AAEA,QAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,CAAC,CAAA,GAAI,cAAA;AAClC,QAAA,QAAA,CAAS,IAAA,CAAK,EAAE,SAAA,EAAW,MAAA,CAAO,aAAa,CAAA,EAAG,KAAA,EAAO,MAAM,CAAA;AAAA,MACjE;AAAA,IACF;AACA,IAAA,MAAA,CAAO,UAAU,CAAA,GAAI,QAAA;AAAA,EACvB,CAAC,CAAA;AAED,EAAA,OAAO,MAAA;AACT,CAAA;AAKO,MAAM,oBAAA,GAAuB,CAClC,SAAA,KAKG;AACH,EAAA,MAAM,MAAA,GAAS,oBAAoB,SAAuC,CAAA;AAC1E,EAAA,MAAM,OAAA,uBAAc,GAAA,EAA6B;AAGjD,EAAA,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,UAAA,EAAY,UAAU,CAAA,KAAM;AAC3D,IAAA,UAAA,CAAW,QAAQ,CAAA,KAAA,KAAS;AAC1B,MAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACjC,QAAA,MAAM,UAAU,KAAA,CAAM,MAAA,CAAO,KAAA,CAAM,SAAS,CAAC,CAAA,GACzC,IAAI,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA,CAAE,OAAA,EAAQ,GAClC,MAAA,CAAO,MAAM,SAAS,CAAA;AAC1B,QAAA,OAAA,CAAQ,GAAA,CAAI,MAAM,SAAA,EAAW;AAAA,UAC3B,MAAM,KAAA,CAAM,SAAA;AAAA,UACZ,SAAA,EAAW;AAAA,SACZ,CAAA;AAAA,MACH;AACA,MAAA,OAAA,CAAQ,IAAI,KAAA,CAAM,SAAS,CAAA,CAAG,UAAU,IAAI,KAAA,CAAM,KAAA;AAAA,IACpD,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AAED,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,SAAA,GAAY,EAAE,SAAS,CAAA;AAC9E;AAUO,MAAM,gBAAA,GAAmB,CAC9B,SAAA,KACiC;AACjC,EAAA,IAAI,cAAc,mBAAA,EAAqB;AACrC,IAAA,OAAO;AAAA,MACL,aAAA,EAAe;AAAA,QACb,GAAA,EAAK,cAAA;AAAA,QACL,KAAA,EAAO;AAAA,OACT;AAAA,MACA,kBAAA,EAAoB;AAAA,QAClB,GAAA,EAAK,wBAAA;AAAA,QACL,KAAA,EAAO;AAAA,OACT;AAAA,MACA,oBAAA,EAAsB;AAAA,QACpB,GAAA,EAAK,0BAAA;AAAA,QACL,KAAA,EAAO;AAAA;AACT,KACF;AAAA,EACF;AAEA,EAAA,IAAI,cAAc,gBAAA,EAAkB;AAClC,IAAA,OAAO;AAAA,MACL,WAAA,EAAa;AAAA,QACX,GAAA,EAAK,aAAA;AAAA,QACL,KAAA,EAAO;AAAA,OACT;AAAA,MACA,UAAA,EAAY;AAAA,QACV,GAAA,EAAK,YAAA;AAAA,QACL,KAAA,EAAO;AAAA,OACT;AAAA,MACA,UAAA,EAAY;AAAA,QACV,GAAA,EAAK,YAAA;AAAA,QACL,KAAA,EAAO;AAAA,OACT;AAAA,MACA,UAAA,EAAY;AAAA,QACV,GAAA,EAAK,YAAA;AAAA,QACL,KAAA,EAAO;AAAA;AACT,KACF;AAAA,EACF;AAEA,EAAA,IAAI,cAAc,KAAA,EAAO;AACvB,IAAA,OAAO;AAAA,MACL,KAAA,EAAO;AAAA,QACL,GAAA,EAAK,UAAA;AAAA,QACL,KAAA,EAAO;AAAA,OACT;AAAA,MACA,QAAA,EAAU;AAAA,QACR,GAAA,EAAK,aAAA;AAAA,QACL,KAAA,EAAO;AAAA,OACT;AAAA,MACA,MAAA,EAAQ;AAAA,QACN,GAAA,EAAK,WAAA;AAAA,QACL,KAAA,EAAO;AAAA;AACT,KACF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,KAAA,EAAO;AAAA,MACL,GAAA,EAAK,aAAA;AAAA,MACL,KAAA,EAAO;AAAA,KACT;AAAA,IACA,QAAA,EAAU;AAAA,MACR,GAAA,EAAK,gBAAA;AAAA,MACL,KAAA,EAAO;AAAA,KACT;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,GAAA,EAAK,cAAA;AAAA,MACL,KAAA,EAAO;AAAA;AACT,GACF;AACF;AAKO,MAAM,cAAA,GAAiB,CAC5B,SAAA,EACA,eAAA,KACW;AACX,EAAA,OAAO,CAAC,eAAA,IAAmB,eAAA,KAAoB,SAAA,GAAY,CAAA,GAAI,GAAA;AACjE;AAKO,MAAM,gBAAA,GAAmB,CAAC,GAAA,KAAwB;AACvD,EAAA,MAAM,IAAA,GAAO,GAAA,CACV,OAAA,CAAQ,UAAA,EAAY,KAAK,CAAA,CACzB,OAAA,CAAQ,IAAA,EAAM,CAAA,GAAA,KAAO,GAAA,CAAI,WAAA,EAAa,CAAA;AACzC,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,KAAK,CAAA,EAAG;AAExB,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,KAAK,CAAA;AAAA,EAClC;AACA,EAAA,OAAO,IAAA;AACT;;;;"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Typography, Button } from '@material-ui/core';
|
|
3
|
+
import Refresh from '@material-ui/icons/Refresh';
|
|
4
|
+
import { makeStyles } from '@material-ui/core/styles';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
|
|
7
|
+
const useRCAActionsStyles = makeStyles((theme) => ({
|
|
8
|
+
statsContainer: {
|
|
9
|
+
marginTop: theme.spacing(2),
|
|
10
|
+
marginBottom: theme.spacing(2),
|
|
11
|
+
display: "flex",
|
|
12
|
+
justifyContent: "space-between",
|
|
13
|
+
alignItems: "center"
|
|
14
|
+
},
|
|
15
|
+
actionsContainer: {
|
|
16
|
+
display: "flex",
|
|
17
|
+
gap: theme.spacing(1),
|
|
18
|
+
alignItems: "center"
|
|
19
|
+
}
|
|
20
|
+
}));
|
|
21
|
+
const RCAActions = memo(
|
|
22
|
+
({ disabled, onRefresh, totalCount }) => {
|
|
23
|
+
const classes = useRCAActionsStyles();
|
|
24
|
+
return /* @__PURE__ */ jsxs(Box, { className: classes.statsContainer, children: [
|
|
25
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "textSecondary", children: totalCount !== void 0 ? `Total reports: ${totalCount}` : "No reports data" }) }),
|
|
26
|
+
/* @__PURE__ */ jsx(Box, { className: classes.actionsContainer, children: /* @__PURE__ */ jsx(
|
|
27
|
+
Button,
|
|
28
|
+
{
|
|
29
|
+
variant: "outlined",
|
|
30
|
+
startIcon: /* @__PURE__ */ jsx(Refresh, {}),
|
|
31
|
+
onClick: onRefresh,
|
|
32
|
+
disabled,
|
|
33
|
+
children: "Refresh"
|
|
34
|
+
}
|
|
35
|
+
) })
|
|
36
|
+
] });
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
RCAActions.displayName = "RCAActions";
|
|
40
|
+
|
|
41
|
+
export { RCAActions };
|
|
42
|
+
//# sourceMappingURL=RCAActions.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RCAActions.esm.js","sources":["../../../src/components/RCA/RCAActions.tsx"],"sourcesContent":["import { Box, Typography, Button } from '@material-ui/core';\nimport RefreshIcon from '@material-ui/icons/Refresh';\nimport { makeStyles, Theme } from '@material-ui/core/styles';\nimport { memo } from 'react';\n\nconst useRCAActionsStyles = makeStyles((theme: Theme) => ({\n statsContainer: {\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n },\n actionsContainer: {\n display: 'flex',\n gap: theme.spacing(1),\n alignItems: 'center',\n },\n}));\n\ninterface RCAActionsProps {\n disabled: boolean;\n onRefresh: () => void;\n totalCount?: number;\n}\n\nexport const RCAActions = memo(\n ({ disabled, onRefresh, totalCount }: RCAActionsProps) => {\n const classes = useRCAActionsStyles();\n\n return (\n <Box className={classes.statsContainer}>\n <Box>\n <Typography variant=\"body2\" color=\"textSecondary\">\n {totalCount !== undefined\n ? `Total reports: ${totalCount}`\n : 'No reports data'}\n </Typography>\n </Box>\n <Box className={classes.actionsContainer}>\n <Button\n variant=\"outlined\"\n startIcon={<RefreshIcon />}\n onClick={onRefresh}\n disabled={disabled}\n >\n Refresh\n </Button>\n </Box>\n </Box>\n );\n },\n);\n\nRCAActions.displayName = 'RCAActions';\n"],"names":["RefreshIcon"],"mappings":";;;;;;AAKA,MAAM,mBAAA,GAAsB,UAAA,CAAW,CAAC,KAAA,MAAkB;AAAA,EACxD,cAAA,EAAgB;AAAA,IACd,SAAA,EAAW,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,IAC1B,YAAA,EAAc,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,IAC7B,OAAA,EAAS,MAAA;AAAA,IACT,cAAA,EAAgB,eAAA;AAAA,IAChB,UAAA,EAAY;AAAA,GACd;AAAA,EACA,gBAAA,EAAkB;AAAA,IAChB,OAAA,EAAS,MAAA;AAAA,IACT,GAAA,EAAK,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,IACpB,UAAA,EAAY;AAAA;AAEhB,CAAA,CAAE,CAAA;AAQK,MAAM,UAAA,GAAa,IAAA;AAAA,EACxB,CAAC,EAAE,QAAA,EAAU,SAAA,EAAW,YAAW,KAAuB;AACxD,IAAA,MAAM,UAAU,mBAAA,EAAoB;AAEpC,IAAA,uBACE,IAAA,CAAC,GAAA,EAAA,EAAI,SAAA,EAAW,OAAA,CAAQ,cAAA,EACtB,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,GAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAAQ,KAAA,EAAM,eAAA,EAC/B,QAAA,EAAA,UAAA,KAAe,MAAA,GACZ,CAAA,eAAA,EAAkB,UAAU,CAAA,CAAA,GAC5B,mBACN,CAAA,EACF,CAAA;AAAA,sBACA,GAAA,CAAC,GAAA,EAAA,EAAI,SAAA,EAAW,OAAA,CAAQ,gBAAA,EACtB,QAAA,kBAAA,GAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,OAAA,EAAQ,UAAA;AAAA,UACR,SAAA,sBAAYA,OAAA,EAAA,EAAY,CAAA;AAAA,UACxB,OAAA,EAAS,SAAA;AAAA,UACT,QAAA;AAAA,UACD,QAAA,EAAA;AAAA;AAAA,OAED,EACF;AAAA,KAAA,EACF,CAAA;AAAA,EAEJ;AACF;AAEA,UAAA,CAAW,WAAA,GAAc,YAAA;;;;"}
|