@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,100 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Grid, TextField, FormControl, InputLabel, Select, MenuItem } from '@material-ui/core';
|
|
3
|
+
import { Skeleton } from '@material-ui/lab';
|
|
4
|
+
import { useDebouncedSearch } from '../../hooks/useDebouncedSearch.esm.js';
|
|
5
|
+
import { RCA_STATUS_OPTIONS } from '../../types.esm.js';
|
|
6
|
+
import { TimeRangeFilter } from '../TimeRangeFilter/TimeRangeFilter.esm.js';
|
|
7
|
+
|
|
8
|
+
const RCAFilters = ({
|
|
9
|
+
filters,
|
|
10
|
+
onFiltersChange,
|
|
11
|
+
environments,
|
|
12
|
+
environmentsLoading,
|
|
13
|
+
disabled = false
|
|
14
|
+
}) => {
|
|
15
|
+
const [searchInput, handleSearchChange] = useDebouncedSearch(
|
|
16
|
+
filters.searchQuery,
|
|
17
|
+
(value) => onFiltersChange({ searchQuery: value })
|
|
18
|
+
);
|
|
19
|
+
const handleEnvironmentChange = (event) => {
|
|
20
|
+
const selectedEnvironment = environments.find(
|
|
21
|
+
(env) => env.uid === event.target.value
|
|
22
|
+
);
|
|
23
|
+
if (selectedEnvironment) {
|
|
24
|
+
onFiltersChange({ environment: selectedEnvironment, searchQuery: "" });
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const handleTimeRangeChange = (next) => {
|
|
28
|
+
onFiltersChange({ ...next, searchQuery: "" });
|
|
29
|
+
};
|
|
30
|
+
const handleStatusChange = (event) => {
|
|
31
|
+
const value = event.target.value;
|
|
32
|
+
onFiltersChange({
|
|
33
|
+
rcaStatus: value ? value : void 0,
|
|
34
|
+
searchQuery: ""
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
return /* @__PURE__ */ jsxs(Grid, { container: true, spacing: 3, children: [
|
|
38
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 3, children: /* @__PURE__ */ jsx(
|
|
39
|
+
TextField,
|
|
40
|
+
{
|
|
41
|
+
fullWidth: true,
|
|
42
|
+
placeholder: "Search RCA reports...",
|
|
43
|
+
variant: "outlined",
|
|
44
|
+
value: searchInput,
|
|
45
|
+
onChange: handleSearchChange,
|
|
46
|
+
disabled
|
|
47
|
+
}
|
|
48
|
+
) }),
|
|
49
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 3, children: /* @__PURE__ */ jsxs(
|
|
50
|
+
FormControl,
|
|
51
|
+
{
|
|
52
|
+
fullWidth: true,
|
|
53
|
+
disabled: disabled || environmentsLoading,
|
|
54
|
+
variant: "outlined",
|
|
55
|
+
children: [
|
|
56
|
+
/* @__PURE__ */ jsx(InputLabel, { id: "environment-label", children: "Environment" }),
|
|
57
|
+
environmentsLoading ? /* @__PURE__ */ jsx(Skeleton, { variant: "rect", height: 56 }) : /* @__PURE__ */ jsx(
|
|
58
|
+
Select,
|
|
59
|
+
{
|
|
60
|
+
value: filters.environment?.uid || "",
|
|
61
|
+
onChange: handleEnvironmentChange,
|
|
62
|
+
labelId: "environment-label",
|
|
63
|
+
label: "Environment",
|
|
64
|
+
children: environments.map((env) => /* @__PURE__ */ jsx(MenuItem, { value: env.uid || env.name, children: env.displayName || env.name }, env.uid || env.name))
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
) }),
|
|
70
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 3, children: /* @__PURE__ */ jsxs(FormControl, { fullWidth: true, disabled, variant: "outlined", children: [
|
|
71
|
+
/* @__PURE__ */ jsx(InputLabel, { id: "status-label", children: "Status" }),
|
|
72
|
+
/* @__PURE__ */ jsxs(
|
|
73
|
+
Select,
|
|
74
|
+
{
|
|
75
|
+
value: filters.rcaStatus || "",
|
|
76
|
+
onChange: handleStatusChange,
|
|
77
|
+
labelId: "status-label",
|
|
78
|
+
label: "Status",
|
|
79
|
+
children: [
|
|
80
|
+
/* @__PURE__ */ jsx(MenuItem, { value: "", children: "All" }),
|
|
81
|
+
RCA_STATUS_OPTIONS.map((option) => /* @__PURE__ */ jsx(MenuItem, { value: option.value, children: option.label }, option.value))
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
] }) }),
|
|
86
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 3, children: /* @__PURE__ */ jsx(
|
|
87
|
+
TimeRangeFilter,
|
|
88
|
+
{
|
|
89
|
+
value: filters.timeRange,
|
|
90
|
+
customStartTime: filters.customStartTime,
|
|
91
|
+
customEndTime: filters.customEndTime,
|
|
92
|
+
onChange: handleTimeRangeChange,
|
|
93
|
+
disabled
|
|
94
|
+
}
|
|
95
|
+
) })
|
|
96
|
+
] });
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export { RCAFilters };
|
|
100
|
+
//# sourceMappingURL=RCAFilters.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RCAFilters.esm.js","sources":["../../../src/components/RCA/RCAFilters.tsx"],"sourcesContent":["import { ChangeEvent } from 'react';\nimport {\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n TextField,\n} from '@material-ui/core';\nimport { Skeleton } from '@material-ui/lab';\nimport { useDebouncedSearch } from '../../hooks/useDebouncedSearch';\nimport {\n type Environment,\n Filters,\n RCA_STATUS_OPTIONS,\n RCAStatus,\n} from '../../types';\nimport { TimeRangeFilter } from '../TimeRangeFilter';\n\ninterface RCAFiltersProps {\n filters: Filters;\n onFiltersChange: (filters: Partial<Filters>) => void;\n environments: Environment[];\n environmentsLoading: boolean;\n disabled?: boolean;\n}\n\nexport const RCAFilters = ({\n filters,\n onFiltersChange,\n environments,\n environmentsLoading,\n disabled = false,\n}: RCAFiltersProps) => {\n const [searchInput, handleSearchChange] = useDebouncedSearch(\n filters.searchQuery,\n value => onFiltersChange({ searchQuery: value }),\n );\n\n const handleEnvironmentChange = (event: ChangeEvent<{ value: unknown }>) => {\n const selectedEnvironment = environments.find(\n env => env.uid === (event.target.value as string),\n );\n if (selectedEnvironment) {\n onFiltersChange({ environment: selectedEnvironment, searchQuery: '' });\n }\n };\n\n const handleTimeRangeChange = (next: {\n timeRange: string;\n customStartTime?: string;\n customEndTime?: string;\n }) => {\n onFiltersChange({ ...next, searchQuery: '' });\n };\n\n const handleStatusChange = (event: ChangeEvent<{ value: unknown }>) => {\n const value = event.target.value as string;\n onFiltersChange({\n rcaStatus: value ? (value as RCAStatus) : undefined,\n searchQuery: '',\n });\n };\n\n return (\n <Grid container spacing={3}>\n <Grid item xs={12} md={3}>\n <TextField\n fullWidth\n placeholder=\"Search RCA reports...\"\n variant=\"outlined\"\n value={searchInput}\n onChange={handleSearchChange}\n disabled={disabled}\n />\n </Grid>\n\n <Grid item xs={12} md={3}>\n <FormControl\n fullWidth\n disabled={disabled || environmentsLoading}\n variant=\"outlined\"\n >\n <InputLabel id=\"environment-label\">Environment</InputLabel>\n {environmentsLoading ? (\n <Skeleton variant=\"rect\" height={56} />\n ) : (\n <Select\n value={filters.environment?.uid || ''}\n onChange={handleEnvironmentChange}\n labelId=\"environment-label\"\n label=\"Environment\"\n >\n {environments.map((env: Environment) => (\n <MenuItem key={env.uid || env.name} value={env.uid || env.name}>\n {env.displayName || env.name}\n </MenuItem>\n ))}\n </Select>\n )}\n </FormControl>\n </Grid>\n\n <Grid item xs={12} md={3}>\n <FormControl fullWidth disabled={disabled} variant=\"outlined\">\n <InputLabel id=\"status-label\">Status</InputLabel>\n <Select\n value={filters.rcaStatus || ''}\n onChange={handleStatusChange}\n labelId=\"status-label\"\n label=\"Status\"\n >\n <MenuItem value=\"\">All</MenuItem>\n {RCA_STATUS_OPTIONS.map(option => (\n <MenuItem key={option.value} value={option.value}>\n {option.label}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n </Grid>\n\n <Grid item xs={12} md={3}>\n <TimeRangeFilter\n value={filters.timeRange}\n customStartTime={filters.customStartTime}\n customEndTime={filters.customEndTime}\n onChange={handleTimeRangeChange}\n disabled={disabled}\n />\n </Grid>\n </Grid>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useMemo, useCallback } from 'react';
|
|
3
|
+
import { Routes, Route } from 'react-router-dom';
|
|
4
|
+
import { Box, Typography, Button } from '@material-ui/core';
|
|
5
|
+
import { RCAFilters } from './RCAFilters.esm.js';
|
|
6
|
+
import { RCAActions } from './RCAActions.esm.js';
|
|
7
|
+
import { RCATable } from './RCATable.esm.js';
|
|
8
|
+
import { useEntity } from '@backstage/plugin-catalog-react';
|
|
9
|
+
import { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';
|
|
10
|
+
import '@backstage/core-plugin-api';
|
|
11
|
+
import { useGetEnvironmentsByNamespace } from '../../hooks/useGetEnvironmentsByNamespace.esm.js';
|
|
12
|
+
import { useUrlFilters } from '../../hooks/useUrlFilters.esm.js';
|
|
13
|
+
import '../../api/ObservabilityApi.esm.js';
|
|
14
|
+
import { useRcaPermission, ForbiddenState } from '@openchoreo/backstage-plugin-react';
|
|
15
|
+
import { useRCAReports } from '../../hooks/useRCAReports.esm.js';
|
|
16
|
+
import '../../utils/urlTimeRange.esm.js';
|
|
17
|
+
import '../../api/RCAAgentApi.esm.js';
|
|
18
|
+
import '../../api/FinOpsAgentApi.esm.js';
|
|
19
|
+
import { Progress } from '@backstage/core-components';
|
|
20
|
+
import { Alert } from '@material-ui/lab';
|
|
21
|
+
import { RCAReport } from './RCAReport.esm.js';
|
|
22
|
+
import { EntityLinkContext } from './RCAReport/EntityLinkContext.esm.js';
|
|
23
|
+
|
|
24
|
+
const RCAListContent = () => {
|
|
25
|
+
const { entity } = useEntity();
|
|
26
|
+
const namespace = entity.metadata.annotations?.[CHOREO_ANNOTATIONS.NAMESPACE];
|
|
27
|
+
const projectName = entity.metadata.name;
|
|
28
|
+
const namespaceValue = useMemo(
|
|
29
|
+
() => ({ namespace: namespace || "default" }),
|
|
30
|
+
[namespace]
|
|
31
|
+
);
|
|
32
|
+
const {
|
|
33
|
+
environments,
|
|
34
|
+
loading: environmentsLoading,
|
|
35
|
+
error: environmentsError
|
|
36
|
+
} = useGetEnvironmentsByNamespace(namespace, projectName);
|
|
37
|
+
const { filters, updateFilters } = useUrlFilters({ environments });
|
|
38
|
+
const {
|
|
39
|
+
reports,
|
|
40
|
+
loading: reportsLoading,
|
|
41
|
+
error: reportsError,
|
|
42
|
+
refresh
|
|
43
|
+
} = useRCAReports(filters, entity);
|
|
44
|
+
const filteredReports = useMemo(() => {
|
|
45
|
+
if (!filters.searchQuery) return reports;
|
|
46
|
+
const q = filters.searchQuery.toLowerCase();
|
|
47
|
+
return reports.filter(
|
|
48
|
+
(r) => (r.alertId || "").toLowerCase().includes(q) || (r.reportId || "").toLowerCase().includes(q) || (r.summary || "").toLowerCase().includes(q) || (r.status || "").toLowerCase().includes(q)
|
|
49
|
+
);
|
|
50
|
+
}, [reports, filters.searchQuery]);
|
|
51
|
+
const handleFiltersChange = useCallback(
|
|
52
|
+
(newFilters) => {
|
|
53
|
+
updateFilters(newFilters);
|
|
54
|
+
},
|
|
55
|
+
[updateFilters]
|
|
56
|
+
);
|
|
57
|
+
const handleRefresh = useCallback(() => {
|
|
58
|
+
refresh();
|
|
59
|
+
}, [refresh]);
|
|
60
|
+
if (environmentsError) {
|
|
61
|
+
return /* @__PURE__ */ jsx(Fragment, {});
|
|
62
|
+
}
|
|
63
|
+
const renderError = (error) => {
|
|
64
|
+
const isObservabilityDisabled = error.includes(
|
|
65
|
+
"Observability is not enabled"
|
|
66
|
+
);
|
|
67
|
+
const isRCAServiceDisabled = error.includes(
|
|
68
|
+
"RCA service is not configured"
|
|
69
|
+
);
|
|
70
|
+
const isInfoAlert = isObservabilityDisabled || isRCAServiceDisabled;
|
|
71
|
+
let errorMessage = error;
|
|
72
|
+
if (isObservabilityDisabled) {
|
|
73
|
+
errorMessage = "Observability is not enabled for this environment. Enable observability and enable the AI RCA agent.";
|
|
74
|
+
} else if (isRCAServiceDisabled) {
|
|
75
|
+
errorMessage = "AI RCA is not configured. Enable it to view RCA reports.";
|
|
76
|
+
}
|
|
77
|
+
return /* @__PURE__ */ jsx(Box, { mt: 2, mb: 2, children: /* @__PURE__ */ jsxs(Alert, { severity: isInfoAlert ? "info" : "error", children: [
|
|
78
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", children: errorMessage }),
|
|
79
|
+
!isInfoAlert && /* @__PURE__ */ jsx(Button, { onClick: handleRefresh, color: "inherit", size: "small", children: "Retry" })
|
|
80
|
+
] }) });
|
|
81
|
+
};
|
|
82
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
83
|
+
reportsLoading && /* @__PURE__ */ jsx(Progress, {}),
|
|
84
|
+
!reportsLoading && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
85
|
+
/* @__PURE__ */ jsx(
|
|
86
|
+
RCAFilters,
|
|
87
|
+
{
|
|
88
|
+
filters,
|
|
89
|
+
onFiltersChange: handleFiltersChange,
|
|
90
|
+
environments,
|
|
91
|
+
environmentsLoading
|
|
92
|
+
}
|
|
93
|
+
),
|
|
94
|
+
reportsError && renderError(reportsError),
|
|
95
|
+
/* @__PURE__ */ jsx(
|
|
96
|
+
RCAActions,
|
|
97
|
+
{
|
|
98
|
+
disabled: reportsLoading,
|
|
99
|
+
onRefresh: handleRefresh,
|
|
100
|
+
totalCount: filteredReports.length
|
|
101
|
+
}
|
|
102
|
+
),
|
|
103
|
+
/* @__PURE__ */ jsx(EntityLinkContext.Provider, { value: namespaceValue, children: /* @__PURE__ */ jsx(RCATable, { reports: filteredReports, loading: reportsLoading }) })
|
|
104
|
+
] })
|
|
105
|
+
] });
|
|
106
|
+
};
|
|
107
|
+
const RCAListView = () => {
|
|
108
|
+
const {
|
|
109
|
+
canViewRca,
|
|
110
|
+
loading: permissionLoading,
|
|
111
|
+
deniedTooltip,
|
|
112
|
+
permissionName
|
|
113
|
+
} = useRcaPermission();
|
|
114
|
+
if (permissionLoading) {
|
|
115
|
+
return /* @__PURE__ */ jsx(Progress, {});
|
|
116
|
+
}
|
|
117
|
+
if (!canViewRca) {
|
|
118
|
+
return /* @__PURE__ */ jsx(
|
|
119
|
+
ForbiddenState,
|
|
120
|
+
{
|
|
121
|
+
message: deniedTooltip,
|
|
122
|
+
permissionName,
|
|
123
|
+
variant: "fullpage"
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return /* @__PURE__ */ jsx(RCAListContent, {});
|
|
128
|
+
};
|
|
129
|
+
const RCAPage = () => {
|
|
130
|
+
return /* @__PURE__ */ jsxs(Routes, { children: [
|
|
131
|
+
/* @__PURE__ */ jsx(Route, { path: "/", element: /* @__PURE__ */ jsx(RCAListView, {}) }),
|
|
132
|
+
/* @__PURE__ */ jsx(Route, { path: "/:reportId", element: /* @__PURE__ */ jsx(RCAReport, {}) })
|
|
133
|
+
] });
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export { RCAPage };
|
|
137
|
+
//# sourceMappingURL=RCAPage.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RCAPage.esm.js","sources":["../../../src/components/RCA/RCAPage.tsx"],"sourcesContent":["import { useCallback, useMemo } from 'react';\nimport { Routes, Route } from 'react-router-dom';\nimport { Box, Typography, Button } from '@material-ui/core';\nimport { RCAFilters } from './RCAFilters';\nimport { RCAActions } from './RCAActions';\nimport { RCATable } from './RCATable';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport type { RCAReportSummary } from '../../types';\nimport {\n useUrlFilters,\n useGetEnvironmentsByNamespace,\n useRCAReports,\n} from '../../hooks';\nimport { Progress } from '@backstage/core-components';\nimport { Alert } from '@material-ui/lab';\nimport {\n useRcaPermission,\n ForbiddenState,\n} from '@openchoreo/backstage-plugin-react';\nimport { RCAReport } from './RCAReport';\nimport { EntityLinkContext } from './RCAReport/EntityLinkContext';\n\nconst RCAListContent = () => {\n const { entity } = useEntity();\n const namespace = entity.metadata.annotations?.[CHOREO_ANNOTATIONS.NAMESPACE];\n const projectName = entity.metadata.name;\n const namespaceValue = useMemo(\n () => ({ namespace: namespace || 'default' }),\n [namespace],\n );\n const {\n environments,\n loading: environmentsLoading,\n error: environmentsError,\n } = useGetEnvironmentsByNamespace(namespace, projectName);\n const { filters, updateFilters } = useUrlFilters({ environments });\n\n const {\n reports,\n loading: reportsLoading,\n error: reportsError,\n refresh,\n } = useRCAReports(filters, entity);\n\n const filteredReports = useMemo((): RCAReportSummary[] => {\n if (!filters.searchQuery) return reports;\n const q = filters.searchQuery.toLowerCase();\n return reports.filter(\n r =>\n (r.alertId || '').toLowerCase().includes(q) ||\n (r.reportId || '').toLowerCase().includes(q) ||\n (r.summary || '').toLowerCase().includes(q) ||\n (r.status || '').toLowerCase().includes(q),\n );\n }, [reports, filters.searchQuery]);\n\n const handleFiltersChange = useCallback(\n (newFilters: Partial<typeof filters>) => {\n updateFilters(newFilters);\n },\n [updateFilters],\n );\n\n const handleRefresh = useCallback(() => {\n refresh();\n }, [refresh]);\n\n if (environmentsError) {\n // TODO: Add a toast notification here\n return <></>;\n }\n\n const renderError = (error: string) => {\n const isObservabilityDisabled = error.includes(\n 'Observability is not enabled',\n );\n const isRCAServiceDisabled = error.includes(\n 'RCA service is not configured',\n );\n const isInfoAlert = isObservabilityDisabled || isRCAServiceDisabled;\n\n let errorMessage = error;\n if (isObservabilityDisabled) {\n errorMessage =\n 'Observability is not enabled for this environment. Enable observability and enable the AI RCA agent.';\n } else if (isRCAServiceDisabled) {\n errorMessage = 'AI RCA is not configured. Enable it to view RCA reports.';\n }\n\n return (\n <Box mt={2} mb={2}>\n <Alert severity={isInfoAlert ? 'info' : 'error'}>\n <Typography variant=\"body1\">{errorMessage}</Typography>\n {!isInfoAlert && (\n <Button onClick={handleRefresh} color=\"inherit\" size=\"small\">\n Retry\n </Button>\n )}\n </Alert>\n </Box>\n );\n };\n\n return (\n <Box>\n {reportsLoading && <Progress />}\n\n {!reportsLoading && (\n <>\n <RCAFilters\n filters={filters}\n onFiltersChange={handleFiltersChange}\n environments={environments}\n environmentsLoading={environmentsLoading}\n />\n\n {reportsError && renderError(reportsError)}\n\n <RCAActions\n disabled={reportsLoading}\n onRefresh={handleRefresh}\n totalCount={filteredReports.length}\n />\n\n <EntityLinkContext.Provider value={namespaceValue}>\n <RCATable reports={filteredReports} loading={reportsLoading} />\n </EntityLinkContext.Provider>\n </>\n )}\n </Box>\n );\n};\n\nconst RCAListView = () => {\n const {\n canViewRca,\n loading: permissionLoading,\n deniedTooltip,\n permissionName,\n } = useRcaPermission();\n\n if (permissionLoading) {\n return <Progress />;\n }\n\n if (!canViewRca) {\n return (\n <ForbiddenState\n message={deniedTooltip}\n permissionName={permissionName}\n variant=\"fullpage\"\n />\n );\n }\n\n return <RCAListContent />;\n};\n\nexport const RCAPage = () => {\n return (\n <Routes>\n <Route path=\"/\" element={<RCAListView />} />\n <Route path=\"/:reportId\" element={<RCAReport />} />\n </Routes>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
const EntityLinkContext = createContext({
|
|
4
|
+
namespace: "default"
|
|
5
|
+
});
|
|
6
|
+
const useEntityLinkContext = () => useContext(EntityLinkContext);
|
|
7
|
+
|
|
8
|
+
export { EntityLinkContext, useEntityLinkContext };
|
|
9
|
+
//# sourceMappingURL=EntityLinkContext.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityLinkContext.esm.js","sources":["../../../../src/components/RCA/RCAReport/EntityLinkContext.tsx"],"sourcesContent":["import { createContext, useContext } from 'react';\n\n/** Provides the OpenChoreo namespace so entity tags can be rendered as catalog links */\nexport const EntityLinkContext = createContext<{ namespace: string }>({\n namespace: 'default',\n});\n\nexport const useEntityLinkContext = () => useContext(EntityLinkContext);\n"],"names":[],"mappings":";;AAGO,MAAM,oBAAoB,aAAA,CAAqC;AAAA,EACpE,SAAA,EAAW;AACb,CAAC;AAEM,MAAM,oBAAA,GAAuB,MAAM,UAAA,CAAW,iBAAiB;;;;"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useMemo, Children, Fragment as Fragment$1 } from 'react';
|
|
3
|
+
import { Link } from '@backstage/core-components';
|
|
4
|
+
import ReactMarkdown from 'react-markdown';
|
|
5
|
+
import { useEntityLinkContext } from './EntityLinkContext.esm.js';
|
|
6
|
+
import { useRCAReportStyles } from './styles.esm.js';
|
|
7
|
+
|
|
8
|
+
const ENTITY_TAG_PATTERN = /(?:<|{{)(comp|proj|env|ns):([^>}]+)(?:>|}})/;
|
|
9
|
+
const ISO_TIMESTAMP_PATTERN = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z/;
|
|
10
|
+
const SPLIT_PATTERN = /((?:<|{{)(?:comp|proj|env|ns):[^>}]+(?:>|}})|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)/g;
|
|
11
|
+
function escapeEntityTags(text) {
|
|
12
|
+
return text.replace(/<((?:comp|proj|env|ns):[^>]+)>/g, "{{$1}}");
|
|
13
|
+
}
|
|
14
|
+
const TAG_TO_KIND = {
|
|
15
|
+
comp: "component",
|
|
16
|
+
proj: "system"
|
|
17
|
+
};
|
|
18
|
+
function formatTimestamp(isoString) {
|
|
19
|
+
try {
|
|
20
|
+
const date = new Date(isoString);
|
|
21
|
+
return date.toLocaleString("en-US", {
|
|
22
|
+
month: "short",
|
|
23
|
+
day: "numeric",
|
|
24
|
+
hour: "numeric",
|
|
25
|
+
minute: "2-digit"
|
|
26
|
+
});
|
|
27
|
+
} catch {
|
|
28
|
+
return isoString;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function stripMarkdown(text) {
|
|
32
|
+
return text.replace(/\*\*(.+?)\*\*/g, "$1").replace(/__(.+?)__/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/_(.+?)_/g, "$1").replace(/`(.+?)`/g, "$1");
|
|
33
|
+
}
|
|
34
|
+
const FormattedText = ({
|
|
35
|
+
text,
|
|
36
|
+
disableLinks = false,
|
|
37
|
+
disableMarkdown = false
|
|
38
|
+
}) => {
|
|
39
|
+
const classes = useRCAReportStyles();
|
|
40
|
+
const { namespace } = useEntityLinkContext();
|
|
41
|
+
const renderSegment = (segment, index) => {
|
|
42
|
+
const tagMatch = segment.match(ENTITY_TAG_PATTERN);
|
|
43
|
+
if (tagMatch) {
|
|
44
|
+
const [, tagType, name] = tagMatch;
|
|
45
|
+
const kind = TAG_TO_KIND[tagType];
|
|
46
|
+
if (!disableLinks && kind) {
|
|
47
|
+
return /* @__PURE__ */ jsx(
|
|
48
|
+
Link,
|
|
49
|
+
{
|
|
50
|
+
to: `/catalog/${namespace}/${kind}/${name}`,
|
|
51
|
+
target: "_blank",
|
|
52
|
+
rel: "noopener noreferrer",
|
|
53
|
+
children: /* @__PURE__ */ jsx("strong", { children: name })
|
|
54
|
+
},
|
|
55
|
+
index
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return /* @__PURE__ */ jsx("strong", { children: name }, index);
|
|
59
|
+
}
|
|
60
|
+
if (ISO_TIMESTAMP_PATTERN.test(segment)) {
|
|
61
|
+
return /* @__PURE__ */ jsx("strong", { children: formatTimestamp(segment) }, index);
|
|
62
|
+
}
|
|
63
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: segment }, index);
|
|
64
|
+
};
|
|
65
|
+
const processChildren = (children) => {
|
|
66
|
+
return Children.map(children, (child) => {
|
|
67
|
+
if (typeof child === "string") {
|
|
68
|
+
const parts = child.split(SPLIT_PATTERN);
|
|
69
|
+
return /* @__PURE__ */ jsx(Fragment, { children: parts.map((part, i) => renderSegment(part, i)) });
|
|
70
|
+
}
|
|
71
|
+
return child;
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
const markdownComponents = useMemo(
|
|
75
|
+
() => ({
|
|
76
|
+
p: ({ children }) => /* @__PURE__ */ jsx("span", { children: processChildren(children) }),
|
|
77
|
+
strong: ({ children }) => /* @__PURE__ */ jsx("strong", { children: processChildren(children) }),
|
|
78
|
+
em: ({ children }) => /* @__PURE__ */ jsx("em", { children: processChildren(children) }),
|
|
79
|
+
code: ({ children }) => /* @__PURE__ */ jsx("code", { children: processChildren(children) })
|
|
80
|
+
}),
|
|
81
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
82
|
+
[namespace, disableLinks]
|
|
83
|
+
);
|
|
84
|
+
if (disableMarkdown) {
|
|
85
|
+
const stripped = stripMarkdown(text);
|
|
86
|
+
const parts = stripped.split(SPLIT_PATTERN);
|
|
87
|
+
return /* @__PURE__ */ jsx(Fragment, { children: parts.map((part, i) => renderSegment(part, i)) });
|
|
88
|
+
}
|
|
89
|
+
const safeText = escapeEntityTags(text);
|
|
90
|
+
return /* @__PURE__ */ jsx("span", { className: classes.markdownContent, children: /* @__PURE__ */ jsx(ReactMarkdown, { components: markdownComponents, children: safeText }) });
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export { FormattedText };
|
|
94
|
+
//# sourceMappingURL=FormattedText.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormattedText.esm.js","sources":["../../../../src/components/RCA/RCAReport/FormattedText.tsx"],"sourcesContent":["import { Children, Fragment, useMemo, type ReactNode } from 'react';\nimport { Link } from '@backstage/core-components';\nimport ReactMarkdown from 'react-markdown';\nimport { useEntityLinkContext } from './EntityLinkContext';\nimport { useRCAReportStyles } from './styles';\n\ninterface FormattedTextProps {\n text: string;\n /** When true, renders entities as bold text instead of clickable links */\n disableLinks?: boolean;\n /** When true, skips markdown processing (for title-ish fields) */\n disableMarkdown?: boolean;\n}\n\n// Entity tag patterns:\n// Raw form: <comp:name>, <proj:name>, <env:name>, <ns:name>\n// Escaped form: {{comp:name}}, {{proj:name}}, etc. (used after pre-processing for markdown safety)\nconst ENTITY_TAG_PATTERN = /(?:<|{{)(comp|proj|env|ns):([^>}]+)(?:>|}})/;\n\n// ISO 8601 timestamp pattern (e.g., 2023-10-05T14:48:00Z or 2023-10-05T14:48:00.123456Z)\nconst ISO_TIMESTAMP_PATTERN = /\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?Z/;\n\n// Combined pattern to split text by entity tags (both forms) and timestamps, keeping delimiters\nconst SPLIT_PATTERN =\n /((?:<|{{)(?:comp|proj|env|ns):[^>}]+(?:>|}})|\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?Z)/g;\n\n// Convert <tag:name> to {{tag:name}} so ReactMarkdown doesn't strip them as HTML\nfunction escapeEntityTags(text: string): string {\n return text.replace(/<((?:comp|proj|env|ns):[^>]+)>/g, '{{$1}}');\n}\n\n// Map tag type to Backstage catalog kind\nconst TAG_TO_KIND: Record<string, string> = {\n comp: 'component',\n proj: 'system',\n};\n\nfunction formatTimestamp(isoString: string): string {\n try {\n const date = new Date(isoString);\n return date.toLocaleString('en-US', {\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n });\n } catch {\n return isoString;\n }\n}\n\n// Strip markdown syntax from text\nfunction stripMarkdown(text: string): string {\n return text\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1') // **bold**\n .replace(/__(.+?)__/g, '$1') // __bold__\n .replace(/\\*(.+?)\\*/g, '$1') // *italic*\n .replace(/_(.+?)_/g, '$1') // _italic_\n .replace(/`(.+?)`/g, '$1'); // `code`\n}\n\n/**\n * Renders text with:\n * - Inline markdown: bold (**), italic (*), code (`)\n * - Entity tags (<comp:name>, <proj:name>, etc.) replaced by catalog links\n * - ISO 8601 timestamps replaced by formatted dates\n */\nexport const FormattedText = ({\n text,\n disableLinks = false,\n disableMarkdown = false,\n}: FormattedTextProps) => {\n const classes = useRCAReportStyles();\n const { namespace } = useEntityLinkContext();\n\n // Render a single text segment (entity tag, timestamp, or plain text)\n const renderSegment = (segment: string, index: number) => {\n const tagMatch = segment.match(ENTITY_TAG_PATTERN);\n if (tagMatch) {\n const [, tagType, name] = tagMatch;\n const kind = TAG_TO_KIND[tagType];\n if (!disableLinks && kind) {\n return (\n <Link\n key={index}\n to={`/catalog/${namespace}/${kind}/${name}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <strong>{name}</strong>\n </Link>\n );\n }\n return <strong key={index}>{name}</strong>;\n }\n\n if (ISO_TIMESTAMP_PATTERN.test(segment)) {\n return <strong key={index}>{formatTimestamp(segment)}</strong>;\n }\n\n return <Fragment key={index}>{segment}</Fragment>;\n };\n\n // Process React children, replacing string nodes with rendered segments\n const processChildren = (children: ReactNode): ReactNode => {\n return Children.map(children, child => {\n if (typeof child === 'string') {\n const parts = child.split(SPLIT_PATTERN);\n return <>{parts.map((part, i) => renderSegment(part, i))}</>;\n }\n return child;\n });\n };\n\n const markdownComponents = useMemo(\n () => ({\n p: ({ children }: { children?: ReactNode }) => (\n <span>{processChildren(children)}</span>\n ),\n strong: ({ children }: { children?: ReactNode }) => (\n <strong>{processChildren(children)}</strong>\n ),\n em: ({ children }: { children?: ReactNode }) => (\n <em>{processChildren(children)}</em>\n ),\n code: ({ children }: { children?: ReactNode }) => (\n <code>{processChildren(children)}</code>\n ),\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [namespace, disableLinks],\n );\n\n // Skip markdown processing for title-ish fields, strip any markdown syntax\n if (disableMarkdown) {\n const stripped = stripMarkdown(text);\n const parts = stripped.split(SPLIT_PATTERN);\n return <>{parts.map((part, i) => renderSegment(part, i))}</>;\n }\n\n // Escape entity tags before markdown parsing to prevent them from being stripped as HTML\n const safeText = escapeEntityTags(text);\n\n return (\n <span className={classes.markdownContent}>\n <ReactMarkdown components={markdownComponents}>{safeText}</ReactMarkdown>\n </span>\n );\n};\n"],"names":["Fragment"],"mappings":"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"}
|