@openchoreo/backstage-plugin-openchoreo-observability 1.1.1 → 1.1.5

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.
@@ -19,6 +19,7 @@ import { useUrlFiltersForAlerts } from '../../hooks/useUrlFiltersForAlerts.esm.j
19
19
  import { useComponentAlerts } from '../../hooks/useComponentAlerts.esm.js';
20
20
  import '../../api/RCAAgentApi.esm.js';
21
21
  import '../../api/FinOpsAgentApi.esm.js';
22
+ import { NoEnvironmentsEmptyState } from '../common/NoEnvironmentsEmptyState.esm.js';
22
23
  import { useRuntimeLogsStyles } from '../RuntimeLogs/styles.esm.js';
23
24
 
24
25
  const ObservabilityAlertsContent = () => {
@@ -183,6 +184,9 @@ const ObservabilityAlertsContent = () => {
183
184
  if (environmentsError) {
184
185
  return /* @__PURE__ */ jsx(Box, { children: renderError(environmentsError) });
185
186
  }
187
+ if (!environmentsLoading && environments.length === 0) {
188
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(NoEnvironmentsEmptyState, { feature: "alerts" }) });
189
+ }
186
190
  return /* @__PURE__ */ jsxs(Box, { children: [
187
191
  /* @__PURE__ */ jsx(
188
192
  AlertsFilter,
@@ -195,7 +199,6 @@ const ObservabilityAlertsContent = () => {
195
199
  }
196
200
  ),
197
201
  alertsError && renderError(alertsError),
198
- !filters.environment && !environmentsLoading && environments.length === 0 && /* @__PURE__ */ jsx(Alert, { severity: "info", className: classes.errorContainer, children: /* @__PURE__ */ jsx(Typography, { variant: "body1", children: "No environments found. Make sure your component is properly configured." }) }),
199
202
  filters.environment && /* @__PURE__ */ jsxs(Fragment, { children: [
200
203
  /* @__PURE__ */ jsx(
201
204
  AlertsActions,
@@ -1 +1 @@
1
- {"version":3,"file":"ObservabilityAlertsPage.esm.js","sources":["../../../src/components/Alerts/ObservabilityAlertsPage.tsx"],"sourcesContent":["import { useEffect, useRef, useMemo, useState, useCallback } from 'react';\nimport { Box, Typography, Button } from '@material-ui/core';\nimport { EmptyState, Progress, WarningIcon } from '@backstage/core-components';\nimport { Alert } from '@material-ui/lab';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport { AlertsFilter } from './AlertsFilter';\nimport { AlertsTable } from './AlertsTable';\nimport { AlertsActions } from './AlertsActions';\nimport {\n useComponentAlerts,\n useGetNamespaceAndProjectByEntity,\n useGetEnvironmentsByNamespace,\n useUrlFiltersForAlerts,\n} from '../../hooks';\nimport { useAlertsPermission } from '@openchoreo/backstage-plugin-react';\nimport { useRuntimeLogsStyles } from '../RuntimeLogs/styles';\nimport type { AlertSummary } from '../../types';\n\nconst ObservabilityAlertsContent = () => {\n const classes = useRuntimeLogsStyles();\n const { entity } = useEntity();\n\n const { namespace, project } = useGetNamespaceAndProjectByEntity(entity);\n\n const {\n environments,\n loading: environmentsLoading,\n error: environmentsError,\n } = useGetEnvironmentsByNamespace(namespace, project);\n\n const { filters, updateFilters } = useUrlFiltersForAlerts({\n environments,\n });\n\n const selectedEnvironment = environments.find(\n env => env.name === filters.environment,\n );\n\n const componentName =\n entity.metadata.annotations?.[CHOREO_ANNOTATIONS.COMPONENT];\n\n const [lastUpdated, setLastUpdated] = useState<Date>(new Date());\n\n const {\n alerts,\n loading: alertsLoading,\n error: alertsError,\n fetchAlerts,\n refresh,\n } = useComponentAlerts(entity, namespace || '', project || '', {\n environment: filters.environment,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n limit: 100,\n sortOrder: filters.sortOrder || 'desc',\n });\n\n const previousFiltersRef = useRef<{\n environment: string;\n timeRange: string;\n customStartTime?: string;\n customEndTime?: string;\n sortOrder?: 'asc' | 'desc';\n } | null>(null);\n\n useEffect(() => {\n const currentFilters = {\n environment: filters.environment,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n sortOrder: filters.sortOrder,\n };\n const filtersChanged =\n previousFiltersRef.current === null ||\n JSON.stringify(previousFiltersRef.current) !==\n JSON.stringify(currentFilters);\n\n if (\n filters.environment &&\n selectedEnvironment &&\n namespace &&\n project &&\n componentName &&\n filtersChanged\n ) {\n fetchAlerts(true);\n setLastUpdated(new Date());\n previousFiltersRef.current = currentFilters;\n }\n }, [\n filters.environment,\n filters.timeRange,\n filters.customStartTime,\n filters.customEndTime,\n filters.sortOrder,\n fetchAlerts,\n selectedEnvironment,\n namespace,\n project,\n componentName,\n ]);\n\n useEffect(() => {\n if (!alertsLoading) setLastUpdated(new Date());\n }, [alertsLoading]);\n\n const handleRefresh = () => {\n refresh();\n setLastUpdated(new Date());\n };\n\n const handleFiltersChange = (newFilters: Partial<typeof filters>) => {\n updateFilters(newFilters);\n };\n\n const filteredAlerts = useMemo((): AlertSummary[] => {\n let result = alerts;\n if (filters.severity && filters.severity.length > 0) {\n result = result.filter(\n a =>\n a.severity &&\n filters.severity!.some(\n s => s.toLowerCase() === (a.severity || '').toLowerCase(),\n ),\n );\n }\n if (filters.searchQuery) {\n const q = filters.searchQuery.toLowerCase();\n result = result.filter(\n a =>\n (a.ruleName || '').toLowerCase().includes(q) ||\n (a.ruleDescription || '').toLowerCase().includes(q) ||\n (a.alertId || '').toLowerCase().includes(q),\n );\n }\n return result;\n }, [alerts, filters.severity, filters.searchQuery]);\n\n // Open the parent project's Incidents tab in a new browser tab, pre-filtered\n // by alertId and with a time range that covers the alert's age.\n const handleViewIncident = useCallback(\n (alert: AlertSummary) => {\n const parentProject =\n (entity.spec?.system as string | undefined) || project || '';\n const catalogNs = entity.metadata.namespace || 'default';\n if (!parentProject) return;\n\n // Pick smallest preset >= age of alert\n const PRESETS: Array<{ value: string; ms: number }> = [\n { value: '10m', ms: 10 * 60 * 1000 },\n { value: '30m', ms: 30 * 60 * 1000 },\n { value: '1h', ms: 60 * 60 * 1000 },\n { value: '24h', ms: 24 * 60 * 60 * 1000 },\n { value: '7d', ms: 7 * 24 * 60 * 60 * 1000 },\n { value: '14d', ms: 14 * 24 * 60 * 60 * 1000 },\n ];\n let timeRange = '1h'; // default\n if (alert.timestamp) {\n const ageMs = Date.now() - new Date(alert.timestamp).getTime();\n const match = PRESETS.find(p => p.ms >= ageMs);\n timeRange = match ? match.value : PRESETS[PRESETS.length - 1].value;\n }\n\n const params = new URLSearchParams({\n search: alert.alertId,\n timeRange,\n ...(filters.environment ? { env: filters.environment } : {}),\n });\n const url = `/catalog/${catalogNs}/system/${parentProject}/incidents?${params.toString()}`;\n window.open(url, '_blank', 'noopener,noreferrer');\n },\n [entity, project, filters.environment],\n );\n\n // Open the parent project's Cost Analysis tab in a new browser tab,\n // pre-filtered by alertId and with a time range that covers the alert's age.\n const handleViewCostAnalysis = useCallback(\n (alert: AlertSummary) => {\n const parentProject =\n (entity.spec?.system as string | undefined) || project || '';\n const catalogNs = entity.metadata.namespace || 'default';\n if (!parentProject) return;\n\n // Pick smallest preset >= age of alert\n const PRESETS: Array<{ value: string; ms: number }> = [\n { value: '10m', ms: 10 * 60 * 1000 },\n { value: '30m', ms: 30 * 60 * 1000 },\n { value: '1h', ms: 60 * 60 * 1000 },\n { value: '24h', ms: 24 * 60 * 60 * 1000 },\n { value: '7d', ms: 7 * 24 * 60 * 60 * 1000 },\n { value: '14d', ms: 14 * 24 * 60 * 60 * 1000 },\n ];\n let timeRange = '1h'; // default\n if (alert.timestamp) {\n const ageMs = Date.now() - new Date(alert.timestamp).getTime();\n const match = PRESETS.find(p => p.ms >= ageMs);\n timeRange = match ? match.value : PRESETS[PRESETS.length - 1].value;\n }\n\n const params = new URLSearchParams({\n q: alert.alertId,\n timeRange,\n ...(filters.environment ? { env: filters.environment } : {}),\n });\n const url = `/catalog/${catalogNs}/system/${parentProject}/cost-analysis?${params.toString()}`;\n window.open(url, '_blank', 'noopener,noreferrer');\n },\n [entity, project, filters.environment],\n );\n\n const renderError = (error: string) => {\n const isObservabilityDisabled = error.includes(\n 'Observability is not enabled',\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. Enable observability to view alerts.'\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 if (environmentsError) {\n return <Box>{renderError(environmentsError)}</Box>;\n }\n\n return (\n <Box>\n <AlertsFilter\n filters={filters}\n onFiltersChange={handleFiltersChange}\n environments={environments}\n environmentsLoading={environmentsLoading}\n disabled={alertsLoading}\n />\n\n {alertsError && renderError(alertsError)}\n\n {!filters.environment &&\n !environmentsLoading &&\n environments.length === 0 && (\n <Alert severity=\"info\" className={classes.errorContainer}>\n <Typography variant=\"body1\">\n No environments found. Make sure your component is properly\n configured.\n </Typography>\n </Alert>\n )}\n\n {filters.environment && (\n <>\n <AlertsActions\n totalCount={filteredAlerts.length}\n disabled={alertsLoading || !filters.environment}\n onRefresh={handleRefresh}\n filters={filters}\n onFiltersChange={handleFiltersChange}\n lastUpdated={lastUpdated}\n />\n\n <AlertsTable\n alerts={filteredAlerts}\n loading={alertsLoading}\n environmentName={\n selectedEnvironment?.displayName || selectedEnvironment?.name\n }\n projectName={project}\n componentName={componentName}\n namespaceName={namespace}\n onViewIncident={handleViewIncident}\n onViewCostAnalysis={handleViewCostAnalysis}\n />\n </>\n )}\n </Box>\n );\n};\n\nexport const ObservabilityAlertsPage = () => {\n const {\n canViewAlerts,\n loading: permissionLoading,\n deniedTooltip,\n } = useAlertsPermission();\n\n if (permissionLoading) return <Progress />;\n\n if (!canViewAlerts) {\n return (\n <EmptyState\n missing=\"data\"\n title=\"Permission Denied\"\n description={\n <Box display=\"flex\" alignItems=\"center\" gridGap={8}>\n <WarningIcon />\n {deniedTooltip}\n </Box>\n }\n />\n );\n }\n\n return <ObservabilityAlertsContent />;\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAmBA,MAAM,6BAA6B,MAAM;AACvC,EAAA,MAAM,UAAU,oBAAA,EAAqB;AACrC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAE7B,EAAA,MAAM,EAAE,SAAA,EAAW,OAAA,EAAQ,GAAI,kCAAkC,MAAM,CAAA;AAEvE,EAAA,MAAM;AAAA,IACJ,YAAA;AAAA,IACA,OAAA,EAAS,mBAAA;AAAA,IACT,KAAA,EAAO;AAAA,GACT,GAAI,6BAAA,CAA8B,SAAA,EAAW,OAAO,CAAA;AAEpD,EAAA,MAAM,EAAE,OAAA,EAAS,aAAA,EAAc,GAAI,sBAAA,CAAuB;AAAA,IACxD;AAAA,GACD,CAAA;AAED,EAAA,MAAM,sBAAsB,YAAA,CAAa,IAAA;AAAA,IACvC,CAAA,GAAA,KAAO,GAAA,CAAI,IAAA,KAAS,OAAA,CAAQ;AAAA,GAC9B;AAEA,EAAA,MAAM,aAAA,GACJ,MAAA,CAAO,QAAA,CAAS,WAAA,GAAc,mBAAmB,SAAS,CAAA;AAE5D,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,IAAI,QAAA,iBAAe,IAAI,MAAM,CAAA;AAE/D,EAAA,MAAM;AAAA,IACJ,MAAA;AAAA,IACA,OAAA,EAAS,aAAA;AAAA,IACT,KAAA,EAAO,WAAA;AAAA,IACP,WAAA;AAAA,IACA;AAAA,MACE,kBAAA,CAAmB,MAAA,EAAQ,SAAA,IAAa,EAAA,EAAI,WAAW,EAAA,EAAI;AAAA,IAC7D,aAAa,OAAA,CAAQ,WAAA;AAAA,IACrB,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,iBAAiB,OAAA,CAAQ,eAAA;AAAA,IACzB,eAAe,OAAA,CAAQ,aAAA;AAAA,IACvB,KAAA,EAAO,GAAA;AAAA,IACP,SAAA,EAAW,QAAQ,SAAA,IAAa;AAAA,GACjC,CAAA;AAED,EAAA,MAAM,kBAAA,GAAqB,OAMjB,IAAI,CAAA;AAEd,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,cAAA,GAAiB;AAAA,MACrB,aAAa,OAAA,CAAQ,WAAA;AAAA,MACrB,WAAW,OAAA,CAAQ,SAAA;AAAA,MACnB,iBAAiB,OAAA,CAAQ,eAAA;AAAA,MACzB,eAAe,OAAA,CAAQ,aAAA;AAAA,MACvB,WAAW,OAAA,CAAQ;AAAA,KACrB;AACA,IAAA,MAAM,cAAA,GACJ,kBAAA,CAAmB,OAAA,KAAY,IAAA,IAC/B,IAAA,CAAK,SAAA,CAAU,kBAAA,CAAmB,OAAO,CAAA,KACvC,IAAA,CAAK,SAAA,CAAU,cAAc,CAAA;AAEjC,IAAA,IACE,QAAQ,WAAA,IACR,mBAAA,IACA,SAAA,IACA,OAAA,IACA,iBACA,cAAA,EACA;AACA,MAAA,WAAA,CAAY,IAAI,CAAA;AAChB,MAAA,cAAA,iBAAe,IAAI,MAAM,CAAA;AACzB,MAAA,kBAAA,CAAmB,OAAA,GAAU,cAAA;AAAA,IAC/B;AAAA,EACF,CAAA,EAAG;AAAA,IACD,OAAA,CAAQ,WAAA;AAAA,IACR,OAAA,CAAQ,SAAA;AAAA,IACR,OAAA,CAAQ,eAAA;AAAA,IACR,OAAA,CAAQ,aAAA;AAAA,IACR,OAAA,CAAQ,SAAA;AAAA,IACR,WAAA;AAAA,IACA,mBAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,aAAA,EAAe,cAAA,iBAAe,IAAI,MAAM,CAAA;AAAA,EAC/C,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA;AAElB,EAAA,MAAM,gBAAgB,MAAM;AAC1B,IAAA,OAAA,EAAQ;AACR,IAAA,cAAA,iBAAe,IAAI,MAAM,CAAA;AAAA,EAC3B,CAAA;AAEA,EAAA,MAAM,mBAAA,GAAsB,CAAC,UAAA,KAAwC;AACnE,IAAA,aAAA,CAAc,UAAU,CAAA;AAAA,EAC1B,CAAA;AAEA,EAAA,MAAM,cAAA,GAAiB,QAAQ,MAAsB;AACnD,IAAA,IAAI,MAAA,GAAS,MAAA;AACb,IAAA,IAAI,OAAA,CAAQ,QAAA,IAAY,OAAA,CAAQ,QAAA,CAAS,SAAS,CAAA,EAAG;AACnD,MAAA,MAAA,GAAS,MAAA,CAAO,MAAA;AAAA,QACd,CAAA,CAAA,KACE,CAAA,CAAE,QAAA,IACF,OAAA,CAAQ,QAAA,CAAU,IAAA;AAAA,UAChB,OAAK,CAAA,CAAE,WAAA,QAAmB,CAAA,CAAE,QAAA,IAAY,IAAI,WAAA;AAAY;AAC1D,OACJ;AAAA,IACF;AACA,IAAA,IAAI,QAAQ,WAAA,EAAa;AACvB,MAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,WAAA,CAAY,WAAA,EAAY;AAC1C,MAAA,MAAA,GAAS,MAAA,CAAO,MAAA;AAAA,QACd,CAAA,CAAA,KAAA,CACG,EAAE,QAAA,IAAY,EAAA,EAAI,aAAY,CAAE,QAAA,CAAS,CAAC,CAAA,IAAA,CAC1C,CAAA,CAAE,eAAA,IAAmB,IAAI,WAAA,EAAY,CAAE,QAAA,CAAS,CAAC,CAAA,IAAA,CACjD,CAAA,CAAE,WAAW,EAAA,EAAI,WAAA,EAAY,CAAE,QAAA,CAAS,CAAC;AAAA,OAC9C;AAAA,IACF;AACA,IAAA,OAAO,MAAA;AAAA,EACT,GAAG,CAAC,MAAA,EAAQ,QAAQ,QAAA,EAAU,OAAA,CAAQ,WAAW,CAAC,CAAA;AAIlD,EAAA,MAAM,kBAAA,GAAqB,WAAA;AAAA,IACzB,CAAC,KAAA,KAAwB;AACvB,MAAA,MAAM,aAAA,GACH,MAAA,CAAO,IAAA,EAAM,MAAA,IAAiC,OAAA,IAAW,EAAA;AAC5D,MAAA,MAAM,SAAA,GAAY,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,SAAA;AAC/C,MAAA,IAAI,CAAC,aAAA,EAAe;AAGpB,MAAA,MAAM,OAAA,GAAgD;AAAA,QACpD,EAAE,KAAA,EAAO,KAAA,EAAO,EAAA,EAAI,EAAA,GAAK,KAAK,GAAA,EAAK;AAAA,QACnC,EAAE,KAAA,EAAO,KAAA,EAAO,EAAA,EAAI,EAAA,GAAK,KAAK,GAAA,EAAK;AAAA,QACnC,EAAE,KAAA,EAAO,IAAA,EAAM,EAAA,EAAI,EAAA,GAAK,KAAK,GAAA,EAAK;AAAA,QAClC,EAAE,KAAA,EAAO,KAAA,EAAO,IAAI,EAAA,GAAK,EAAA,GAAK,KAAK,GAAA,EAAK;AAAA,QACxC,EAAE,OAAO,IAAA,EAAM,EAAA,EAAI,IAAI,EAAA,GAAK,EAAA,GAAK,KAAK,GAAA,EAAK;AAAA,QAC3C,EAAE,OAAO,KAAA,EAAO,EAAA,EAAI,KAAK,EAAA,GAAK,EAAA,GAAK,KAAK,GAAA;AAAK,OAC/C;AACA,MAAA,IAAI,SAAA,GAAY,IAAA;AAChB,MAAA,IAAI,MAAM,SAAA,EAAW;AACnB,QAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI,GAAI,IAAI,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA,CAAE,OAAA,EAAQ;AAC7D,QAAA,MAAM,QAAQ,OAAA,CAAQ,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,MAAM,KAAK,CAAA;AAC7C,QAAA,SAAA,GAAY,QAAQ,KAAA,CAAM,KAAA,GAAQ,QAAQ,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA,CAAE,KAAA;AAAA,MAChE;AAEA,MAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB;AAAA,QACjC,QAAQ,KAAA,CAAM,OAAA;AAAA,QACd,SAAA;AAAA,QACA,GAAI,QAAQ,WAAA,GAAc,EAAE,KAAK,OAAA,CAAQ,WAAA,KAAgB;AAAC,OAC3D,CAAA;AACD,MAAA,MAAM,GAAA,GAAM,YAAY,SAAS,CAAA,QAAA,EAAW,aAAa,CAAA,WAAA,EAAc,MAAA,CAAO,UAAU,CAAA,CAAA;AACxF,MAAA,MAAA,CAAO,IAAA,CAAK,GAAA,EAAK,QAAA,EAAU,qBAAqB,CAAA;AAAA,IAClD,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,OAAA,EAAS,OAAA,CAAQ,WAAW;AAAA,GACvC;AAIA,EAAA,MAAM,sBAAA,GAAyB,WAAA;AAAA,IAC7B,CAAC,KAAA,KAAwB;AACvB,MAAA,MAAM,aAAA,GACH,MAAA,CAAO,IAAA,EAAM,MAAA,IAAiC,OAAA,IAAW,EAAA;AAC5D,MAAA,MAAM,SAAA,GAAY,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,SAAA;AAC/C,MAAA,IAAI,CAAC,aAAA,EAAe;AAGpB,MAAA,MAAM,OAAA,GAAgD;AAAA,QACpD,EAAE,KAAA,EAAO,KAAA,EAAO,EAAA,EAAI,EAAA,GAAK,KAAK,GAAA,EAAK;AAAA,QACnC,EAAE,KAAA,EAAO,KAAA,EAAO,EAAA,EAAI,EAAA,GAAK,KAAK,GAAA,EAAK;AAAA,QACnC,EAAE,KAAA,EAAO,IAAA,EAAM,EAAA,EAAI,EAAA,GAAK,KAAK,GAAA,EAAK;AAAA,QAClC,EAAE,KAAA,EAAO,KAAA,EAAO,IAAI,EAAA,GAAK,EAAA,GAAK,KAAK,GAAA,EAAK;AAAA,QACxC,EAAE,OAAO,IAAA,EAAM,EAAA,EAAI,IAAI,EAAA,GAAK,EAAA,GAAK,KAAK,GAAA,EAAK;AAAA,QAC3C,EAAE,OAAO,KAAA,EAAO,EAAA,EAAI,KAAK,EAAA,GAAK,EAAA,GAAK,KAAK,GAAA;AAAK,OAC/C;AACA,MAAA,IAAI,SAAA,GAAY,IAAA;AAChB,MAAA,IAAI,MAAM,SAAA,EAAW;AACnB,QAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI,GAAI,IAAI,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA,CAAE,OAAA,EAAQ;AAC7D,QAAA,MAAM,QAAQ,OAAA,CAAQ,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,MAAM,KAAK,CAAA;AAC7C,QAAA,SAAA,GAAY,QAAQ,KAAA,CAAM,KAAA,GAAQ,QAAQ,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA,CAAE,KAAA;AAAA,MAChE;AAEA,MAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB;AAAA,QACjC,GAAG,KAAA,CAAM,OAAA;AAAA,QACT,SAAA;AAAA,QACA,GAAI,QAAQ,WAAA,GAAc,EAAE,KAAK,OAAA,CAAQ,WAAA,KAAgB;AAAC,OAC3D,CAAA;AACD,MAAA,MAAM,GAAA,GAAM,YAAY,SAAS,CAAA,QAAA,EAAW,aAAa,CAAA,eAAA,EAAkB,MAAA,CAAO,UAAU,CAAA,CAAA;AAC5F,MAAA,MAAA,CAAO,IAAA,CAAK,GAAA,EAAK,QAAA,EAAU,qBAAqB,CAAA;AAAA,IAClD,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,OAAA,EAAS,OAAA,CAAQ,WAAW;AAAA,GACvC;AAEA,EAAA,MAAM,WAAA,GAAc,CAAC,KAAA,KAAkB;AACrC,IAAA,MAAM,0BAA0B,KAAA,CAAM,QAAA;AAAA,MACpC;AAAA,KACF;AACA,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,QAAA,EAAU,0BAA0B,MAAA,GAAS,OAAA;AAAA,QAC7C,WAAW,OAAA,CAAQ,cAAA;AAAA,QAEnB,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EACjB,QAAA,EAAA,uBAAA,GACG,0FACA,KAAA,EACN,CAAA;AAAA,UACC,CAAC,uBAAA,oBACA,GAAA,CAAC,MAAA,EAAA,EAAO,OAAA,EAAS,eAAe,KAAA,EAAM,SAAA,EAAU,IAAA,EAAK,OAAA,EAAQ,QAAA,EAAA,OAAA,EAE7D;AAAA;AAAA;AAAA,KAEJ;AAAA,EAEJ,CAAA;AAEA,EAAA,IAAI,iBAAA,EAAmB;AACrB,IAAA,uBAAO,GAAA,CAAC,GAAA,EAAA,EAAK,QAAA,EAAA,WAAA,CAAY,iBAAiB,CAAA,EAAE,CAAA;AAAA,EAC9C;AAEA,EAAA,4BACG,GAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,YAAA;AAAA,MAAA;AAAA,QACC,OAAA;AAAA,QACA,eAAA,EAAiB,mBAAA;AAAA,QACjB,YAAA;AAAA,QACA,mBAAA;AAAA,QACA,QAAA,EAAU;AAAA;AAAA,KACZ;AAAA,IAEC,WAAA,IAAe,YAAY,WAAW,CAAA;AAAA,IAEtC,CAAC,QAAQ,WAAA,IACR,CAAC,uBACD,YAAA,CAAa,MAAA,KAAW,qBACtB,GAAA,CAAC,KAAA,EAAA,EAAM,UAAS,MAAA,EAAO,SAAA,EAAW,QAAQ,cAAA,EACxC,QAAA,kBAAA,GAAA,CAAC,cAAW,OAAA,EAAQ,OAAA,EAAQ,qFAG5B,CAAA,EACF,CAAA;AAAA,IAGH,OAAA,CAAQ,+BACP,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,aAAA;AAAA,QAAA;AAAA,UACC,YAAY,cAAA,CAAe,MAAA;AAAA,UAC3B,QAAA,EAAU,aAAA,IAAiB,CAAC,OAAA,CAAQ,WAAA;AAAA,UACpC,SAAA,EAAW,aAAA;AAAA,UACX,OAAA;AAAA,UACA,eAAA,EAAiB,mBAAA;AAAA,UACjB;AAAA;AAAA,OACF;AAAA,sBAEA,GAAA;AAAA,QAAC,WAAA;AAAA,QAAA;AAAA,UACC,MAAA,EAAQ,cAAA;AAAA,UACR,OAAA,EAAS,aAAA;AAAA,UACT,eAAA,EACE,mBAAA,EAAqB,WAAA,IAAe,mBAAA,EAAqB,IAAA;AAAA,UAE3D,WAAA,EAAa,OAAA;AAAA,UACb,aAAA;AAAA,UACA,aAAA,EAAe,SAAA;AAAA,UACf,cAAA,EAAgB,kBAAA;AAAA,UAChB,kBAAA,EAAoB;AAAA;AAAA;AACtB,KAAA,EACF;AAAA,GAAA,EAEJ,CAAA;AAEJ,CAAA;AAEO,MAAM,0BAA0B,MAAM;AAC3C,EAAA,MAAM;AAAA,IACJ,aAAA;AAAA,IACA,OAAA,EAAS,iBAAA;AAAA,IACT;AAAA,MACE,mBAAA,EAAoB;AAExB,EAAA,IAAI,iBAAA,EAAmB,uBAAO,GAAA,CAAC,QAAA,EAAA,EAAS,CAAA;AAExC,EAAA,IAAI,CAAC,aAAA,EAAe;AAClB,IAAA,uBACE,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAQ,MAAA;AAAA,QACR,KAAA,EAAM,mBAAA;AAAA,QACN,WAAA,uBACG,GAAA,EAAA,EAAI,OAAA,EAAQ,QAAO,UAAA,EAAW,QAAA,EAAS,SAAS,CAAA,EAC/C,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,WAAA,EAAA,EAAY,CAAA;AAAA,UACZ;AAAA,SAAA,EACH;AAAA;AAAA,KAEJ;AAAA,EAEJ;AAEA,EAAA,2BAAQ,0BAAA,EAAA,EAA2B,CAAA;AACrC;;;;"}
1
+ {"version":3,"file":"ObservabilityAlertsPage.esm.js","sources":["../../../src/components/Alerts/ObservabilityAlertsPage.tsx"],"sourcesContent":["import { useEffect, useRef, useMemo, useState, useCallback } from 'react';\nimport { Box, Typography, Button } from '@material-ui/core';\nimport { EmptyState, Progress, WarningIcon } from '@backstage/core-components';\nimport { Alert } from '@material-ui/lab';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport { AlertsFilter } from './AlertsFilter';\nimport { AlertsTable } from './AlertsTable';\nimport { AlertsActions } from './AlertsActions';\nimport {\n useComponentAlerts,\n useGetNamespaceAndProjectByEntity,\n useGetEnvironmentsByNamespace,\n useUrlFiltersForAlerts,\n} from '../../hooks';\nimport { useAlertsPermission } from '@openchoreo/backstage-plugin-react';\nimport { NoEnvironmentsEmptyState } from '../common';\nimport { useRuntimeLogsStyles } from '../RuntimeLogs/styles';\nimport type { AlertSummary } from '../../types';\n\nconst ObservabilityAlertsContent = () => {\n const classes = useRuntimeLogsStyles();\n const { entity } = useEntity();\n\n const { namespace, project } = useGetNamespaceAndProjectByEntity(entity);\n\n const {\n environments,\n loading: environmentsLoading,\n error: environmentsError,\n } = useGetEnvironmentsByNamespace(namespace, project);\n\n const { filters, updateFilters } = useUrlFiltersForAlerts({\n environments,\n });\n\n const selectedEnvironment = environments.find(\n env => env.name === filters.environment,\n );\n\n const componentName =\n entity.metadata.annotations?.[CHOREO_ANNOTATIONS.COMPONENT];\n\n const [lastUpdated, setLastUpdated] = useState<Date>(new Date());\n\n const {\n alerts,\n loading: alertsLoading,\n error: alertsError,\n fetchAlerts,\n refresh,\n } = useComponentAlerts(entity, namespace || '', project || '', {\n environment: filters.environment,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n limit: 100,\n sortOrder: filters.sortOrder || 'desc',\n });\n\n const previousFiltersRef = useRef<{\n environment: string;\n timeRange: string;\n customStartTime?: string;\n customEndTime?: string;\n sortOrder?: 'asc' | 'desc';\n } | null>(null);\n\n useEffect(() => {\n const currentFilters = {\n environment: filters.environment,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n sortOrder: filters.sortOrder,\n };\n const filtersChanged =\n previousFiltersRef.current === null ||\n JSON.stringify(previousFiltersRef.current) !==\n JSON.stringify(currentFilters);\n\n if (\n filters.environment &&\n selectedEnvironment &&\n namespace &&\n project &&\n componentName &&\n filtersChanged\n ) {\n fetchAlerts(true);\n setLastUpdated(new Date());\n previousFiltersRef.current = currentFilters;\n }\n }, [\n filters.environment,\n filters.timeRange,\n filters.customStartTime,\n filters.customEndTime,\n filters.sortOrder,\n fetchAlerts,\n selectedEnvironment,\n namespace,\n project,\n componentName,\n ]);\n\n useEffect(() => {\n if (!alertsLoading) setLastUpdated(new Date());\n }, [alertsLoading]);\n\n const handleRefresh = () => {\n refresh();\n setLastUpdated(new Date());\n };\n\n const handleFiltersChange = (newFilters: Partial<typeof filters>) => {\n updateFilters(newFilters);\n };\n\n const filteredAlerts = useMemo((): AlertSummary[] => {\n let result = alerts;\n if (filters.severity && filters.severity.length > 0) {\n result = result.filter(\n a =>\n a.severity &&\n filters.severity!.some(\n s => s.toLowerCase() === (a.severity || '').toLowerCase(),\n ),\n );\n }\n if (filters.searchQuery) {\n const q = filters.searchQuery.toLowerCase();\n result = result.filter(\n a =>\n (a.ruleName || '').toLowerCase().includes(q) ||\n (a.ruleDescription || '').toLowerCase().includes(q) ||\n (a.alertId || '').toLowerCase().includes(q),\n );\n }\n return result;\n }, [alerts, filters.severity, filters.searchQuery]);\n\n // Open the parent project's Incidents tab in a new browser tab, pre-filtered\n // by alertId and with a time range that covers the alert's age.\n const handleViewIncident = useCallback(\n (alert: AlertSummary) => {\n const parentProject =\n (entity.spec?.system as string | undefined) || project || '';\n const catalogNs = entity.metadata.namespace || 'default';\n if (!parentProject) return;\n\n // Pick smallest preset >= age of alert\n const PRESETS: Array<{ value: string; ms: number }> = [\n { value: '10m', ms: 10 * 60 * 1000 },\n { value: '30m', ms: 30 * 60 * 1000 },\n { value: '1h', ms: 60 * 60 * 1000 },\n { value: '24h', ms: 24 * 60 * 60 * 1000 },\n { value: '7d', ms: 7 * 24 * 60 * 60 * 1000 },\n { value: '14d', ms: 14 * 24 * 60 * 60 * 1000 },\n ];\n let timeRange = '1h'; // default\n if (alert.timestamp) {\n const ageMs = Date.now() - new Date(alert.timestamp).getTime();\n const match = PRESETS.find(p => p.ms >= ageMs);\n timeRange = match ? match.value : PRESETS[PRESETS.length - 1].value;\n }\n\n const params = new URLSearchParams({\n search: alert.alertId,\n timeRange,\n ...(filters.environment ? { env: filters.environment } : {}),\n });\n const url = `/catalog/${catalogNs}/system/${parentProject}/incidents?${params.toString()}`;\n window.open(url, '_blank', 'noopener,noreferrer');\n },\n [entity, project, filters.environment],\n );\n\n // Open the parent project's Cost Analysis tab in a new browser tab,\n // pre-filtered by alertId and with a time range that covers the alert's age.\n const handleViewCostAnalysis = useCallback(\n (alert: AlertSummary) => {\n const parentProject =\n (entity.spec?.system as string | undefined) || project || '';\n const catalogNs = entity.metadata.namespace || 'default';\n if (!parentProject) return;\n\n // Pick smallest preset >= age of alert\n const PRESETS: Array<{ value: string; ms: number }> = [\n { value: '10m', ms: 10 * 60 * 1000 },\n { value: '30m', ms: 30 * 60 * 1000 },\n { value: '1h', ms: 60 * 60 * 1000 },\n { value: '24h', ms: 24 * 60 * 60 * 1000 },\n { value: '7d', ms: 7 * 24 * 60 * 60 * 1000 },\n { value: '14d', ms: 14 * 24 * 60 * 60 * 1000 },\n ];\n let timeRange = '1h'; // default\n if (alert.timestamp) {\n const ageMs = Date.now() - new Date(alert.timestamp).getTime();\n const match = PRESETS.find(p => p.ms >= ageMs);\n timeRange = match ? match.value : PRESETS[PRESETS.length - 1].value;\n }\n\n const params = new URLSearchParams({\n q: alert.alertId,\n timeRange,\n ...(filters.environment ? { env: filters.environment } : {}),\n });\n const url = `/catalog/${catalogNs}/system/${parentProject}/cost-analysis?${params.toString()}`;\n window.open(url, '_blank', 'noopener,noreferrer');\n },\n [entity, project, filters.environment],\n );\n\n const renderError = (error: string) => {\n const isObservabilityDisabled = error.includes(\n 'Observability is not enabled',\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. Enable observability to view alerts.'\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 if (environmentsError) {\n return <Box>{renderError(environmentsError)}</Box>;\n }\n\n if (!environmentsLoading && environments.length === 0) {\n return (\n <Box>\n <NoEnvironmentsEmptyState feature=\"alerts\" />\n </Box>\n );\n }\n\n return (\n <Box>\n <AlertsFilter\n filters={filters}\n onFiltersChange={handleFiltersChange}\n environments={environments}\n environmentsLoading={environmentsLoading}\n disabled={alertsLoading}\n />\n\n {alertsError && renderError(alertsError)}\n\n {filters.environment && (\n <>\n <AlertsActions\n totalCount={filteredAlerts.length}\n disabled={alertsLoading || !filters.environment}\n onRefresh={handleRefresh}\n filters={filters}\n onFiltersChange={handleFiltersChange}\n lastUpdated={lastUpdated}\n />\n\n <AlertsTable\n alerts={filteredAlerts}\n loading={alertsLoading}\n environmentName={\n selectedEnvironment?.displayName || selectedEnvironment?.name\n }\n projectName={project}\n componentName={componentName}\n namespaceName={namespace}\n onViewIncident={handleViewIncident}\n onViewCostAnalysis={handleViewCostAnalysis}\n />\n </>\n )}\n </Box>\n );\n};\n\nexport const ObservabilityAlertsPage = () => {\n const {\n canViewAlerts,\n loading: permissionLoading,\n deniedTooltip,\n } = useAlertsPermission();\n\n if (permissionLoading) return <Progress />;\n\n if (!canViewAlerts) {\n return (\n <EmptyState\n missing=\"data\"\n title=\"Permission Denied\"\n description={\n <Box display=\"flex\" alignItems=\"center\" gridGap={8}>\n <WarningIcon />\n {deniedTooltip}\n </Box>\n }\n />\n );\n }\n\n return <ObservabilityAlertsContent />;\n};\n"],"names":[],"mappings":"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"}
@@ -18,6 +18,7 @@ import '../../api/RCAAgentApi.esm.js';
18
18
  import '../../api/FinOpsAgentApi.esm.js';
19
19
  import { Progress } from '@backstage/core-components';
20
20
  import { Alert } from '@material-ui/lab';
21
+ import { NoEnvironmentsEmptyState } from '../common/NoEnvironmentsEmptyState.esm.js';
21
22
  import { CostAnalysisReport } from './CostAnalysisReport.esm.js';
22
23
  import { EntityLinkContext } from '../RCA/RCAReport/EntityLinkContext.esm.js';
23
24
 
@@ -57,6 +58,9 @@ const CostAnalysisListContent = () => {
57
58
  const handleRefresh = useCallback(() => {
58
59
  refresh();
59
60
  }, [refresh]);
61
+ if (!environmentsLoading && !environmentsError && environments.length === 0) {
62
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(NoEnvironmentsEmptyState, { feature: "cost analysis reports" }) });
63
+ }
60
64
  const renderError = (error) => {
61
65
  const isObservabilityDisabled = error.includes(
62
66
  "Observability is not enabled"
@@ -1 +1 @@
1
- {"version":3,"file":"CostAnalysisPage.esm.js","sources":["../../../src/components/CostAnalysis/CostAnalysisPage.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 '../RCA/RCAFilters';\nimport { RCAActions } from '../RCA/RCAActions';\nimport { CostAnalysisTable } from './CostAnalysisTable';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport type { FinOpsReportSummary } from '../../types';\nimport {\n useUrlFilters,\n useGetEnvironmentsByNamespace,\n useFinOpsReports,\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 { CostAnalysisReport } from './CostAnalysisReport';\nimport { EntityLinkContext } from '../RCA/RCAReport/EntityLinkContext';\n\nconst CostAnalysisListContent = () => {\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 } = useFinOpsReports(filters, entity);\n\n const filteredReports = useMemo((): FinOpsReportSummary[] => {\n if (!filters.searchQuery) return reports;\n const q = filters.searchQuery.toLowerCase();\n return reports.filter(\n r =>\n (r.reportId || '').toLowerCase().includes(q) ||\n (r.component || '').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 const renderError = (error: string) => {\n const isObservabilityDisabled = error.includes(\n 'Observability is not enabled',\n );\n const isFinOpsServiceDisabled = error.includes(\n 'FinOps service is not configured',\n );\n const isInfoAlert = isObservabilityDisabled || isFinOpsServiceDisabled;\n\n let errorMessage = error;\n if (isObservabilityDisabled) {\n errorMessage =\n 'Observability is not enabled for this environment. Enable observability and enable the FinOps agent.';\n } else if (isFinOpsServiceDisabled) {\n errorMessage =\n 'FinOps Agent is not configured. Enable it to view cost analysis 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 {environmentsError && (\n <Box mt={2} mb={2}>\n <Alert severity=\"warning\">\n <Typography variant=\"body1\">\n Failed to load environments. Environment filtering may not be\n available.\n </Typography>\n </Alert>\n </Box>\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 <CostAnalysisTable\n reports={filteredReports}\n loading={reportsLoading}\n />\n </EntityLinkContext.Provider>\n </>\n )}\n </Box>\n );\n};\n\nconst CostAnalysisListView = () => {\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 <CostAnalysisListContent />;\n};\n\nexport const CostAnalysisPage = () => {\n return (\n <Routes>\n <Route path=\"/\" element={<CostAnalysisListView />} />\n <Route path=\"/:reportId\" element={<CostAnalysisReport />} />\n </Routes>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CostAnalysisPage.esm.js","sources":["../../../src/components/CostAnalysis/CostAnalysisPage.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 '../RCA/RCAFilters';\nimport { RCAActions } from '../RCA/RCAActions';\nimport { CostAnalysisTable } from './CostAnalysisTable';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport type { FinOpsReportSummary } from '../../types';\nimport {\n useUrlFilters,\n useGetEnvironmentsByNamespace,\n useFinOpsReports,\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 { NoEnvironmentsEmptyState } from '../common';\nimport { CostAnalysisReport } from './CostAnalysisReport';\nimport { EntityLinkContext } from '../RCA/RCAReport/EntityLinkContext';\n\nconst CostAnalysisListContent = () => {\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 } = useFinOpsReports(filters, entity);\n\n const filteredReports = useMemo((): FinOpsReportSummary[] => {\n if (!filters.searchQuery) return reports;\n const q = filters.searchQuery.toLowerCase();\n return reports.filter(\n r =>\n (r.reportId || '').toLowerCase().includes(q) ||\n (r.component || '').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 (!environmentsLoading && !environmentsError && environments.length === 0) {\n return (\n <Box>\n <NoEnvironmentsEmptyState feature=\"cost analysis reports\" />\n </Box>\n );\n }\n\n const renderError = (error: string) => {\n const isObservabilityDisabled = error.includes(\n 'Observability is not enabled',\n );\n const isFinOpsServiceDisabled = error.includes(\n 'FinOps service is not configured',\n );\n const isInfoAlert = isObservabilityDisabled || isFinOpsServiceDisabled;\n\n let errorMessage = error;\n if (isObservabilityDisabled) {\n errorMessage =\n 'Observability is not enabled for this environment. Enable observability and enable the FinOps agent.';\n } else if (isFinOpsServiceDisabled) {\n errorMessage =\n 'FinOps Agent is not configured. Enable it to view cost analysis 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 {environmentsError && (\n <Box mt={2} mb={2}>\n <Alert severity=\"warning\">\n <Typography variant=\"body1\">\n Failed to load environments. Environment filtering may not be\n available.\n </Typography>\n </Alert>\n </Box>\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 <CostAnalysisTable\n reports={filteredReports}\n loading={reportsLoading}\n />\n </EntityLinkContext.Provider>\n </>\n )}\n </Box>\n );\n};\n\nconst CostAnalysisListView = () => {\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 <CostAnalysisListContent />;\n};\n\nexport const CostAnalysisPage = () => {\n return (\n <Routes>\n <Route path=\"/\" element={<CostAnalysisListView />} />\n <Route path=\"/:reportId\" element={<CostAnalysisReport />} />\n </Routes>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
3
- import { Box, Typography, Snackbar, Button } from '@material-ui/core';
3
+ import { Box, Snackbar, Typography, Button } from '@material-ui/core';
4
4
  import { Progress, EmptyState, WarningIcon } from '@backstage/core-components';
5
5
  import { Alert } from '@material-ui/lab';
6
6
  import { useEntity } from '@backstage/plugin-catalog-react';
@@ -18,6 +18,7 @@ import { useGetComponentsByProject } from '../../hooks/useGetComponentsByProject
18
18
  import { useUrlFiltersForIncidents } from '../../hooks/useUrlFiltersForIncidents.esm.js';
19
19
  import { useProjectIncidents } from '../../hooks/useProjectIncidents.esm.js';
20
20
  import { useUpdateIncident } from '../../hooks/useUpdateIncident.esm.js';
21
+ import { NoEnvironmentsEmptyState } from '../common/NoEnvironmentsEmptyState.esm.js';
21
22
  import { useRuntimeLogsStyles } from '../RuntimeLogs/styles.esm.js';
22
23
 
23
24
  const ObservabilityProjectIncidentsContent = () => {
@@ -201,6 +202,9 @@ const ObservabilityProjectIncidentsContent = () => {
201
202
  if (componentsError) {
202
203
  return /* @__PURE__ */ jsx(Box, { children: renderError(componentsError) });
203
204
  }
205
+ if (!environmentsLoading && environments.length === 0) {
206
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(NoEnvironmentsEmptyState, { feature: "incidents" }) });
207
+ }
204
208
  return /* @__PURE__ */ jsxs(Box, { children: [
205
209
  /* @__PURE__ */ jsx(
206
210
  IncidentsFilter,
@@ -215,7 +219,6 @@ const ObservabilityProjectIncidentsContent = () => {
215
219
  }
216
220
  ),
217
221
  incidentsError && renderError(incidentsError),
218
- !filters.environment && !environmentsLoading && environments.length === 0 && /* @__PURE__ */ jsx(Alert, { severity: "info", className: classes.errorContainer, children: /* @__PURE__ */ jsx(Typography, { variant: "body1", children: "No environments found for this project." }) }),
219
222
  filters.environment && /* @__PURE__ */ jsxs(Fragment, { children: [
220
223
  /* @__PURE__ */ jsx(
221
224
  IncidentsActions,
@@ -1 +1 @@
1
- {"version":3,"file":"ObservabilityProjectIncidentsPage.esm.js","sources":["../../../src/components/Incidents/ObservabilityProjectIncidentsPage.tsx"],"sourcesContent":["import { useEffect, useRef, useMemo, useState, useCallback } from 'react';\nimport { Box, Typography, Button, Snackbar } from '@material-ui/core';\nimport { EmptyState, Progress, WarningIcon } from '@backstage/core-components';\nimport { Alert } from '@material-ui/lab';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport { IncidentsFilter } from './IncidentsFilter';\nimport { IncidentsTable } from './IncidentsTable';\nimport { IncidentsActions } from './IncidentsActions';\nimport {\n useGetEnvironmentsByNamespace,\n useGetComponentsByProject,\n useProjectIncidents,\n useUrlFiltersForIncidents,\n useUpdateIncident,\n} from '../../hooks';\nimport { useIncidentsPermission } from '@openchoreo/backstage-plugin-react';\nimport { useRuntimeLogsStyles } from '../RuntimeLogs/styles';\nimport type { IncidentSummary } from '../../types';\n\nconst ObservabilityProjectIncidentsContent = () => {\n const classes = useRuntimeLogsStyles();\n const { entity } = useEntity();\n\n const namespace =\n entity.metadata.annotations?.[CHOREO_ANNOTATIONS.NAMESPACE] || '';\n const projectName = entity.metadata.name || '';\n\n const {\n environments,\n loading: environmentsLoading,\n error: environmentsError,\n } = useGetEnvironmentsByNamespace(namespace, projectName);\n\n const {\n components,\n loading: componentsLoading,\n error: componentsError,\n } = useGetComponentsByProject(entity);\n\n const { filters, updateFilters } = useUrlFiltersForIncidents({\n environments,\n });\n\n const selectedEnvironment = environments.find(\n env => env.name === filters.environment,\n );\n\n const [lastUpdated, setLastUpdated] = useState<Date>(new Date());\n const [updatingIncidentId, setUpdatingIncidentId] = useState<string | null>(\n null,\n );\n const [snackbarMessage, setSnackbarMessage] = useState<string | null>(null);\n\n const { updateIncident } = useUpdateIncident();\n\n const {\n incidents,\n loading: incidentsLoading,\n error: incidentsError,\n fetchIncidents,\n refresh,\n } = useProjectIncidents(entity, {\n environment: filters.environment,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n components: filters.components,\n sortOrder: filters.sortOrder || 'desc',\n });\n\n const previousFiltersRef = useRef<{\n environment: string;\n timeRange: string;\n customStartTime?: string;\n customEndTime?: string;\n components: string[];\n sortOrder?: 'asc' | 'desc';\n } | null>(null);\n\n useEffect(() => {\n const currentFilters = {\n environment: filters.environment,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n components: filters.components || [],\n sortOrder: filters.sortOrder,\n };\n const filtersChanged =\n previousFiltersRef.current === null ||\n JSON.stringify(previousFiltersRef.current) !==\n JSON.stringify(currentFilters);\n\n if (\n filters.environment &&\n selectedEnvironment &&\n namespace &&\n projectName &&\n filtersChanged\n ) {\n fetchIncidents(true);\n setLastUpdated(new Date());\n previousFiltersRef.current = currentFilters;\n }\n }, [\n filters.environment,\n filters.timeRange,\n filters.customStartTime,\n filters.customEndTime,\n filters.components,\n filters.sortOrder,\n fetchIncidents,\n selectedEnvironment,\n namespace,\n projectName,\n ]);\n\n useEffect(() => {\n if (!incidentsLoading) setLastUpdated(new Date());\n }, [incidentsLoading]);\n\n const handleRefresh = () => {\n refresh();\n setLastUpdated(new Date());\n };\n\n const handleFiltersChange = (newFilters: Partial<typeof filters>) => {\n updateFilters(newFilters);\n };\n\n const filteredIncidents = useMemo((): IncidentSummary[] => {\n let result = incidents;\n if (filters.status && filters.status.length > 0) {\n result = result.filter(\n i =>\n i.status &&\n filters.status!.some(\n s => s.toLowerCase() === (i.status || '').toLowerCase(),\n ),\n );\n }\n if (filters.searchQuery) {\n const q = filters.searchQuery.toLowerCase();\n result = result.filter(\n i =>\n (i.description || '').toLowerCase().includes(q) ||\n (i.incidentId || '').toLowerCase().includes(q) ||\n (i.alertId || '').toLowerCase().includes(q) ||\n (i.componentName || '').toLowerCase().includes(q),\n );\n }\n return result;\n }, [incidents, filters.status, filters.searchQuery]);\n\n // Open the RCA Reports tab of this project entity in a new browser tab,\n // pre-filtered by environment, time range, and alert ID.\n const handleViewRCA = useCallback(\n (incident: IncidentSummary) => {\n const catalogNs = entity.metadata.namespace || 'default';\n const params = new URLSearchParams({\n ...(filters.environment ? { env: filters.environment } : {}),\n ...(filters.timeRange ? { timeRange: filters.timeRange } : {}),\n ...(incident.alertId ? { q: incident.alertId } : {}),\n });\n const query = params.toString();\n const url = `/catalog/${catalogNs}/system/${projectName}/rca-reports${\n query ? `?${query}` : ''\n }`;\n window.open(url, '_blank', 'noopener,noreferrer');\n },\n [entity, projectName, filters.environment, filters.timeRange],\n );\n\n // Open the Cost Analysis tab of this project entity in a new browser tab,\n // pre-filtered by environment and time range.\n const handleViewCostAnalysis = useCallback(\n (_incident: IncidentSummary) => {\n const catalogNs = entity.metadata.namespace || 'default';\n const params = new URLSearchParams({\n ...(filters.environment ? { env: filters.environment } : {}),\n ...(filters.timeRange ? { timeRange: filters.timeRange } : {}),\n });\n const query = params.toString();\n const url = `/catalog/${catalogNs}/system/${projectName}/cost-analysis${\n query ? `?${query}` : ''\n }`;\n window.open(url, '_blank', 'noopener,noreferrer');\n },\n [entity, projectName, filters.environment, filters.timeRange],\n );\n\n const handleAcknowledge = useCallback(\n async (incident: IncidentSummary) => {\n setUpdatingIncidentId(incident.incidentId);\n try {\n await updateIncident(incident, 'acknowledged');\n refresh();\n setLastUpdated(new Date());\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n setSnackbarMessage(\n `Failed to acknowledge incident ${incident.incidentId}: ${detail}`,\n );\n } finally {\n setUpdatingIncidentId(null);\n }\n },\n [updateIncident, refresh],\n );\n\n const handleResolve = useCallback(\n async (incident: IncidentSummary) => {\n setUpdatingIncidentId(incident.incidentId);\n try {\n await updateIncident(incident, 'resolved');\n refresh();\n setLastUpdated(new Date());\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n setSnackbarMessage(\n `Failed to resolve incident ${incident.incidentId}: ${detail}`,\n );\n } finally {\n setUpdatingIncidentId(null);\n }\n },\n [updateIncident, refresh],\n );\n\n const renderError = (error: string) => {\n const isObservabilityDisabled = error.includes(\n 'Observability is not enabled',\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 project. Enable observability to view incidents.'\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 if (environmentsError) {\n return <Box>{renderError(environmentsError)}</Box>;\n }\n\n if (componentsError) {\n return <Box>{renderError(componentsError)}</Box>;\n }\n\n return (\n <Box>\n <IncidentsFilter\n filters={filters}\n onFiltersChange={handleFiltersChange}\n environments={environments}\n environmentsLoading={environmentsLoading}\n components={components}\n componentsLoading={componentsLoading}\n disabled={incidentsLoading}\n />\n\n {incidentsError && renderError(incidentsError)}\n\n {!filters.environment &&\n !environmentsLoading &&\n environments.length === 0 && (\n <Alert severity=\"info\" className={classes.errorContainer}>\n <Typography variant=\"body1\">\n No environments found for this project.\n </Typography>\n </Alert>\n )}\n\n {filters.environment && (\n <>\n <IncidentsActions\n totalCount={filteredIncidents.length}\n disabled={incidentsLoading || !filters.environment}\n onRefresh={handleRefresh}\n filters={filters}\n onFiltersChange={handleFiltersChange}\n lastUpdated={lastUpdated}\n />\n\n <IncidentsTable\n incidents={filteredIncidents}\n loading={incidentsLoading}\n namespaceName={namespace}\n projectName={projectName}\n environmentName={\n selectedEnvironment?.displayName || selectedEnvironment?.name\n }\n onViewRCA={handleViewRCA}\n onViewCostAnalysis={handleViewCostAnalysis}\n onAcknowledge={handleAcknowledge}\n onResolve={handleResolve}\n updatingIncidentId={updatingIncidentId}\n />\n </>\n )}\n\n <Snackbar\n open={!!snackbarMessage}\n autoHideDuration={6000}\n onClose={() => setSnackbarMessage(null)}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}\n >\n <Alert severity=\"error\" onClose={() => setSnackbarMessage(null)}>\n {snackbarMessage}\n </Alert>\n </Snackbar>\n </Box>\n );\n};\n\nexport const ObservabilityProjectIncidentsPage = () => {\n const {\n canViewIncidents,\n loading: permissionLoading,\n deniedTooltip,\n } = useIncidentsPermission();\n\n if (permissionLoading) return <Progress />;\n\n if (!canViewIncidents) {\n return (\n <EmptyState\n missing=\"data\"\n title=\"Permission Denied\"\n description={\n <Box display=\"flex\" alignItems=\"center\" gridGap={8}>\n <WarningIcon />\n {deniedTooltip}\n </Box>\n }\n />\n );\n }\n\n return <ObservabilityProjectIncidentsContent />;\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ObservabilityProjectIncidentsPage.esm.js","sources":["../../../src/components/Incidents/ObservabilityProjectIncidentsPage.tsx"],"sourcesContent":["import { useEffect, useRef, useMemo, useState, useCallback } from 'react';\nimport { Box, Typography, Button, Snackbar } from '@material-ui/core';\nimport { EmptyState, Progress, WarningIcon } from '@backstage/core-components';\nimport { Alert } from '@material-ui/lab';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport { IncidentsFilter } from './IncidentsFilter';\nimport { IncidentsTable } from './IncidentsTable';\nimport { IncidentsActions } from './IncidentsActions';\nimport {\n useGetEnvironmentsByNamespace,\n useGetComponentsByProject,\n useProjectIncidents,\n useUrlFiltersForIncidents,\n useUpdateIncident,\n} from '../../hooks';\nimport { useIncidentsPermission } from '@openchoreo/backstage-plugin-react';\nimport { NoEnvironmentsEmptyState } from '../common';\nimport { useRuntimeLogsStyles } from '../RuntimeLogs/styles';\nimport type { IncidentSummary } from '../../types';\n\nconst ObservabilityProjectIncidentsContent = () => {\n const classes = useRuntimeLogsStyles();\n const { entity } = useEntity();\n\n const namespace =\n entity.metadata.annotations?.[CHOREO_ANNOTATIONS.NAMESPACE] || '';\n const projectName = entity.metadata.name || '';\n\n const {\n environments,\n loading: environmentsLoading,\n error: environmentsError,\n } = useGetEnvironmentsByNamespace(namespace, projectName);\n\n const {\n components,\n loading: componentsLoading,\n error: componentsError,\n } = useGetComponentsByProject(entity);\n\n const { filters, updateFilters } = useUrlFiltersForIncidents({\n environments,\n });\n\n const selectedEnvironment = environments.find(\n env => env.name === filters.environment,\n );\n\n const [lastUpdated, setLastUpdated] = useState<Date>(new Date());\n const [updatingIncidentId, setUpdatingIncidentId] = useState<string | null>(\n null,\n );\n const [snackbarMessage, setSnackbarMessage] = useState<string | null>(null);\n\n const { updateIncident } = useUpdateIncident();\n\n const {\n incidents,\n loading: incidentsLoading,\n error: incidentsError,\n fetchIncidents,\n refresh,\n } = useProjectIncidents(entity, {\n environment: filters.environment,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n components: filters.components,\n sortOrder: filters.sortOrder || 'desc',\n });\n\n const previousFiltersRef = useRef<{\n environment: string;\n timeRange: string;\n customStartTime?: string;\n customEndTime?: string;\n components: string[];\n sortOrder?: 'asc' | 'desc';\n } | null>(null);\n\n useEffect(() => {\n const currentFilters = {\n environment: filters.environment,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n components: filters.components || [],\n sortOrder: filters.sortOrder,\n };\n const filtersChanged =\n previousFiltersRef.current === null ||\n JSON.stringify(previousFiltersRef.current) !==\n JSON.stringify(currentFilters);\n\n if (\n filters.environment &&\n selectedEnvironment &&\n namespace &&\n projectName &&\n filtersChanged\n ) {\n fetchIncidents(true);\n setLastUpdated(new Date());\n previousFiltersRef.current = currentFilters;\n }\n }, [\n filters.environment,\n filters.timeRange,\n filters.customStartTime,\n filters.customEndTime,\n filters.components,\n filters.sortOrder,\n fetchIncidents,\n selectedEnvironment,\n namespace,\n projectName,\n ]);\n\n useEffect(() => {\n if (!incidentsLoading) setLastUpdated(new Date());\n }, [incidentsLoading]);\n\n const handleRefresh = () => {\n refresh();\n setLastUpdated(new Date());\n };\n\n const handleFiltersChange = (newFilters: Partial<typeof filters>) => {\n updateFilters(newFilters);\n };\n\n const filteredIncidents = useMemo((): IncidentSummary[] => {\n let result = incidents;\n if (filters.status && filters.status.length > 0) {\n result = result.filter(\n i =>\n i.status &&\n filters.status!.some(\n s => s.toLowerCase() === (i.status || '').toLowerCase(),\n ),\n );\n }\n if (filters.searchQuery) {\n const q = filters.searchQuery.toLowerCase();\n result = result.filter(\n i =>\n (i.description || '').toLowerCase().includes(q) ||\n (i.incidentId || '').toLowerCase().includes(q) ||\n (i.alertId || '').toLowerCase().includes(q) ||\n (i.componentName || '').toLowerCase().includes(q),\n );\n }\n return result;\n }, [incidents, filters.status, filters.searchQuery]);\n\n // Open the RCA Reports tab of this project entity in a new browser tab,\n // pre-filtered by environment, time range, and alert ID.\n const handleViewRCA = useCallback(\n (incident: IncidentSummary) => {\n const catalogNs = entity.metadata.namespace || 'default';\n const params = new URLSearchParams({\n ...(filters.environment ? { env: filters.environment } : {}),\n ...(filters.timeRange ? { timeRange: filters.timeRange } : {}),\n ...(incident.alertId ? { q: incident.alertId } : {}),\n });\n const query = params.toString();\n const url = `/catalog/${catalogNs}/system/${projectName}/rca-reports${\n query ? `?${query}` : ''\n }`;\n window.open(url, '_blank', 'noopener,noreferrer');\n },\n [entity, projectName, filters.environment, filters.timeRange],\n );\n\n // Open the Cost Analysis tab of this project entity in a new browser tab,\n // pre-filtered by environment and time range.\n const handleViewCostAnalysis = useCallback(\n (_incident: IncidentSummary) => {\n const catalogNs = entity.metadata.namespace || 'default';\n const params = new URLSearchParams({\n ...(filters.environment ? { env: filters.environment } : {}),\n ...(filters.timeRange ? { timeRange: filters.timeRange } : {}),\n });\n const query = params.toString();\n const url = `/catalog/${catalogNs}/system/${projectName}/cost-analysis${\n query ? `?${query}` : ''\n }`;\n window.open(url, '_blank', 'noopener,noreferrer');\n },\n [entity, projectName, filters.environment, filters.timeRange],\n );\n\n const handleAcknowledge = useCallback(\n async (incident: IncidentSummary) => {\n setUpdatingIncidentId(incident.incidentId);\n try {\n await updateIncident(incident, 'acknowledged');\n refresh();\n setLastUpdated(new Date());\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n setSnackbarMessage(\n `Failed to acknowledge incident ${incident.incidentId}: ${detail}`,\n );\n } finally {\n setUpdatingIncidentId(null);\n }\n },\n [updateIncident, refresh],\n );\n\n const handleResolve = useCallback(\n async (incident: IncidentSummary) => {\n setUpdatingIncidentId(incident.incidentId);\n try {\n await updateIncident(incident, 'resolved');\n refresh();\n setLastUpdated(new Date());\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n setSnackbarMessage(\n `Failed to resolve incident ${incident.incidentId}: ${detail}`,\n );\n } finally {\n setUpdatingIncidentId(null);\n }\n },\n [updateIncident, refresh],\n );\n\n const renderError = (error: string) => {\n const isObservabilityDisabled = error.includes(\n 'Observability is not enabled',\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 project. Enable observability to view incidents.'\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 if (environmentsError) {\n return <Box>{renderError(environmentsError)}</Box>;\n }\n\n if (componentsError) {\n return <Box>{renderError(componentsError)}</Box>;\n }\n\n if (!environmentsLoading && environments.length === 0) {\n return (\n <Box>\n <NoEnvironmentsEmptyState feature=\"incidents\" />\n </Box>\n );\n }\n\n return (\n <Box>\n <IncidentsFilter\n filters={filters}\n onFiltersChange={handleFiltersChange}\n environments={environments}\n environmentsLoading={environmentsLoading}\n components={components}\n componentsLoading={componentsLoading}\n disabled={incidentsLoading}\n />\n\n {incidentsError && renderError(incidentsError)}\n\n {filters.environment && (\n <>\n <IncidentsActions\n totalCount={filteredIncidents.length}\n disabled={incidentsLoading || !filters.environment}\n onRefresh={handleRefresh}\n filters={filters}\n onFiltersChange={handleFiltersChange}\n lastUpdated={lastUpdated}\n />\n\n <IncidentsTable\n incidents={filteredIncidents}\n loading={incidentsLoading}\n namespaceName={namespace}\n projectName={projectName}\n environmentName={\n selectedEnvironment?.displayName || selectedEnvironment?.name\n }\n onViewRCA={handleViewRCA}\n onViewCostAnalysis={handleViewCostAnalysis}\n onAcknowledge={handleAcknowledge}\n onResolve={handleResolve}\n updatingIncidentId={updatingIncidentId}\n />\n </>\n )}\n\n <Snackbar\n open={!!snackbarMessage}\n autoHideDuration={6000}\n onClose={() => setSnackbarMessage(null)}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}\n >\n <Alert severity=\"error\" onClose={() => setSnackbarMessage(null)}>\n {snackbarMessage}\n </Alert>\n </Snackbar>\n </Box>\n );\n};\n\nexport const ObservabilityProjectIncidentsPage = () => {\n const {\n canViewIncidents,\n loading: permissionLoading,\n deniedTooltip,\n } = useIncidentsPermission();\n\n if (permissionLoading) return <Progress />;\n\n if (!canViewIncidents) {\n return (\n <EmptyState\n missing=\"data\"\n title=\"Permission Denied\"\n description={\n <Box display=\"flex\" alignItems=\"center\" gridGap={8}>\n <WarningIcon />\n {deniedTooltip}\n </Box>\n }\n />\n );\n }\n\n return <ObservabilityProjectIncidentsContent />;\n};\n"],"names":[],"mappings":"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"}
@@ -21,6 +21,7 @@ import '../../api/RCAAgentApi.esm.js';
21
21
  import '../../api/FinOpsAgentApi.esm.js';
22
22
  import { useObservabilityMetricsPageStyles } from './styles.esm.js';
23
23
  import { Alert } from '@material-ui/lab';
24
+ import { NoEnvironmentsEmptyState } from '../common/NoEnvironmentsEmptyState.esm.js';
24
25
 
25
26
  const ObservabilityMetricsContent = () => {
26
27
  const classes = useObservabilityMetricsPageStyles();
@@ -76,6 +77,9 @@ const ObservabilityMetricsContent = () => {
76
77
  if (environmentsError) {
77
78
  return /* @__PURE__ */ jsx(Fragment, {});
78
79
  }
80
+ if (!environmentsLoading && environments.length === 0) {
81
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(NoEnvironmentsEmptyState, { feature: "metrics" }) });
82
+ }
79
83
  const isLoading = environmentsLoading;
80
84
  const renderError = (error) => {
81
85
  const isObservabilityDisabled = error.includes(
@@ -1 +1 @@
1
- {"version":3,"file":"ObservabilityMetricsPage.esm.js","sources":["../../../src/components/Metrics/ObservabilityMetricsPage.tsx"],"sourcesContent":["import { useEffect, useState } 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 [refreshNonce, setRefreshNonce] = useState(0);\n\n const handleFiltersChange = (newFilters: Partial<typeof filters>) => {\n updateFilters(newFilters);\n };\n\n const handleRefresh = () => {\n refresh();\n setRefreshNonce(prev => prev + 1);\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 refreshNonce={refreshNonce}\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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"}
1
+ {"version":3,"file":"ObservabilityMetricsPage.esm.js","sources":["../../../src/components/Metrics/ObservabilityMetricsPage.tsx"],"sourcesContent":["import { useEffect, useState } 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';\nimport { NoEnvironmentsEmptyState } from '../common';\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 [refreshNonce, setRefreshNonce] = useState(0);\n\n const handleFiltersChange = (newFilters: Partial<typeof filters>) => {\n updateFilters(newFilters);\n };\n\n const handleRefresh = () => {\n refresh();\n setRefreshNonce(prev => prev + 1);\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 if (!environmentsLoading && environments.length === 0) {\n return (\n <Box>\n <NoEnvironmentsEmptyState feature=\"metrics\" />\n </Box>\n );\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 refreshNonce={refreshNonce}\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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"}
@@ -18,6 +18,7 @@ import '../../api/RCAAgentApi.esm.js';
18
18
  import '../../api/FinOpsAgentApi.esm.js';
19
19
  import { Progress } from '@backstage/core-components';
20
20
  import { Alert } from '@material-ui/lab';
21
+ import { NoEnvironmentsEmptyState } from '../common/NoEnvironmentsEmptyState.esm.js';
21
22
  import { RCAReport } from './RCAReport.esm.js';
22
23
  import { EntityLinkContext } from './RCAReport/EntityLinkContext.esm.js';
23
24
 
@@ -60,6 +61,9 @@ const RCAListContent = () => {
60
61
  if (environmentsError) {
61
62
  return /* @__PURE__ */ jsx(Fragment, {});
62
63
  }
64
+ if (!environmentsLoading && environments.length === 0) {
65
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(NoEnvironmentsEmptyState, { feature: "RCA reports" }) });
66
+ }
63
67
  const renderError = (error) => {
64
68
  const isObservabilityDisabled = error.includes(
65
69
  "Observability is not enabled"
@@ -1 +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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"}
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 { NoEnvironmentsEmptyState } from '../common';\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 if (!environmentsLoading && environments.length === 0) {\n return (\n <Box>\n <NoEnvironmentsEmptyState feature=\"RCA reports\" />\n </Box>\n );\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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"}
@@ -19,6 +19,7 @@ import { useProjectRuntimeLogs } from '../../hooks/useProjectRuntimeLogs.esm.js'
19
19
  import { useUrlFiltersForRuntimeLogs } from '../../hooks/useUrlFiltersForRuntimeLogs.esm.js';
20
20
  import '../../api/RCAAgentApi.esm.js';
21
21
  import '../../api/FinOpsAgentApi.esm.js';
22
+ import { NoEnvironmentsEmptyState } from '../common/NoEnvironmentsEmptyState.esm.js';
22
23
  import { useRuntimeLogsStyles } from './styles.esm.js';
23
24
  import { LogEntryField } from './types.esm.js';
24
25
 
@@ -148,6 +149,9 @@ const ObservabilityProjectRuntimeLogsContent = ({
148
149
  if (componentsError) {
149
150
  return /* @__PURE__ */ jsx(Box, { children: renderError(componentsError) });
150
151
  }
152
+ if (!environmentsLoading && environments.length === 0) {
153
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(NoEnvironmentsEmptyState, { feature: "logs" }) });
154
+ }
151
155
  return /* @__PURE__ */ jsxs(Box, { children: [
152
156
  /* @__PURE__ */ jsx(
153
157
  LogsFilter,
@@ -162,7 +166,6 @@ const ObservabilityProjectRuntimeLogsContent = ({
162
166
  }
163
167
  ),
164
168
  logsError && renderError(logsError),
165
- !filters.environment && !environmentsLoading && environments.length === 0 && /* @__PURE__ */ jsx(Alert, { severity: "info", className: classes.errorContainer, children: /* @__PURE__ */ jsx(Typography, { variant: "body1", children: "No environments found for this project." }) }),
166
169
  filters.environment && /* @__PURE__ */ jsxs(Fragment, { children: [
167
170
  /* @__PURE__ */ jsx(
168
171
  LogsActions,
@@ -1 +1 @@
1
- {"version":3,"file":"ObservabilityProjectRuntimeLogsPage.esm.js","sources":["../../../src/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { Box, Typography, Button } from '@material-ui/core';\nimport { Progress } from '@backstage/core-components';\nimport { Alert } from '@material-ui/lab';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport { LogsFilter } from './LogsFilter';\nimport { LogsTable } from './LogsTable';\nimport { LogsActions } from './LogsActions';\nimport {\n useGetEnvironmentsByNamespace,\n useGetComponentsByProject,\n useProjectRuntimeLogs,\n useUrlFiltersForRuntimeLogs,\n} from '../../hooks';\nimport {\n useInfiniteScroll,\n useLogsPermission,\n ForbiddenState,\n} from '@openchoreo/backstage-plugin-react';\nimport { useRuntimeLogsStyles } from './styles';\nimport { LogEntryField } from './types';\nimport type { RenderLogRowAction } from './LogEntry';\n\nexport interface ObservabilityProjectRuntimeLogsPageProps {\n renderRowAction?: RenderLogRowAction;\n}\n\nconst ObservabilityProjectRuntimeLogsContent = ({\n renderRowAction,\n}: ObservabilityProjectRuntimeLogsPageProps) => {\n const classes = useRuntimeLogsStyles();\n const { entity } = useEntity();\n\n const namespace =\n entity.metadata.annotations?.[CHOREO_ANNOTATIONS.NAMESPACE] || '';\n const projectName = entity.metadata.name || '';\n\n const {\n environments,\n loading: environmentsLoading,\n error: environmentsError,\n } = useGetEnvironmentsByNamespace(namespace, projectName);\n\n const {\n components,\n loading: componentsLoading,\n error: componentsError,\n } = useGetComponentsByProject(entity);\n\n const { filters, updateFilters } = useUrlFiltersForRuntimeLogs({\n environments,\n });\n\n const selectedEnvironment = environments.find(\n env => env.name === filters.environment,\n );\n const tableSelectedFields = useMemo(() => {\n const withoutComponent = filters.selectedFields.filter(\n field => field !== LogEntryField.ComponentName,\n );\n const logIndex = withoutComponent.indexOf(LogEntryField.Log);\n\n if (logIndex === -1) {\n return [...withoutComponent, LogEntryField.ComponentName];\n }\n\n return [\n ...withoutComponent.slice(0, logIndex),\n LogEntryField.ComponentName,\n ...withoutComponent.slice(logIndex),\n ];\n }, [filters.selectedFields]);\n\n const [lastUpdated, setLastUpdated] = useState<Date>(new Date());\n\n const {\n logs,\n loading: logsLoading,\n error: logsError,\n totalCount,\n hasMore,\n fetchLogs,\n loadMore,\n refresh,\n clearLogs,\n } = useProjectRuntimeLogs(filters, entity, {\n environmentName: filters.environment,\n namespaceName: namespace,\n projectName,\n limit: 50,\n });\n\n const { loadingRef } = useInfiniteScroll(loadMore, hasMore, logsLoading);\n\n const previousFiltersRef = useRef<{\n environment: string;\n logLevel: string[];\n timeRange: string;\n customStartTime?: string;\n customEndTime?: string;\n searchQuery?: string;\n sortOrder?: 'asc' | 'desc';\n components: string[];\n } | null>(null);\n\n useEffect(() => {\n const currentFilters = {\n environment: filters.environment,\n logLevel: filters.logLevel,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n searchQuery: filters.searchQuery,\n sortOrder: filters.sortOrder,\n components: filters.components || [],\n };\n\n const filtersChanged =\n previousFiltersRef.current === null ||\n JSON.stringify(previousFiltersRef.current) !==\n JSON.stringify(currentFilters);\n\n if (\n filters.environment &&\n selectedEnvironment &&\n namespace &&\n projectName &&\n filtersChanged\n ) {\n if (filters.logLevel.length === 0) {\n clearLogs();\n } else {\n fetchLogs(true);\n setLastUpdated(new Date());\n }\n previousFiltersRef.current = currentFilters;\n }\n }, [\n filters.environment,\n filters.logLevel,\n filters.timeRange,\n filters.customStartTime,\n filters.customEndTime,\n filters.searchQuery,\n filters.sortOrder,\n filters.components,\n fetchLogs,\n clearLogs,\n selectedEnvironment,\n namespace,\n projectName,\n ]);\n\n useEffect(() => {\n if (!logsLoading) {\n setLastUpdated(new Date());\n }\n }, [logsLoading]);\n\n const handleRefresh = () => {\n refresh();\n setLastUpdated(new Date());\n };\n\n const handleFiltersChange = (newFilters: Partial<typeof filters>) => {\n updateFilters(newFilters);\n };\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 project in the current environment. Enable observability to view runtime logs.'\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 if (environmentsError) {\n return <Box>{renderError(environmentsError)}</Box>;\n }\n\n if (componentsError) {\n return <Box>{renderError(componentsError)}</Box>;\n }\n\n return (\n <Box>\n <LogsFilter\n filters={filters}\n onFiltersChange={handleFiltersChange}\n environments={environments}\n environmentsLoading={environmentsLoading}\n components={components}\n componentsLoading={componentsLoading}\n disabled={logsLoading}\n />\n\n {logsError && renderError(logsError)}\n\n {!filters.environment &&\n !environmentsLoading &&\n environments.length === 0 && (\n <Alert severity=\"info\" className={classes.errorContainer}>\n <Typography variant=\"body1\">\n No environments found for this project.\n </Typography>\n </Alert>\n )}\n\n {filters.environment && (\n <>\n <LogsActions\n totalCount={totalCount}\n disabled={logsLoading || !filters.environment}\n onRefresh={handleRefresh}\n filters={filters}\n onFiltersChange={handleFiltersChange}\n lastUpdated={lastUpdated}\n />\n\n <LogsTable\n selectedFields={tableSelectedFields}\n logs={logs}\n loading={logsLoading}\n hasMore={hasMore}\n loadingRef={loadingRef}\n environmentName={\n selectedEnvironment?.displayName || selectedEnvironment?.name\n }\n projectName={projectName}\n renderRowAction={renderRowAction}\n />\n </>\n )}\n </Box>\n );\n};\n\nexport const ObservabilityProjectRuntimeLogsPage = ({\n renderRowAction,\n}: ObservabilityProjectRuntimeLogsPageProps) => {\n const {\n canViewLogs,\n loading: permissionLoading,\n deniedTooltip,\n permissionName,\n } = useLogsPermission();\n\n if (permissionLoading) {\n return <Progress />;\n }\n\n if (!canViewLogs) {\n return (\n <ForbiddenState\n message={deniedTooltip}\n permissionName={permissionName}\n variant=\"fullpage\"\n />\n );\n }\n\n return (\n <ObservabilityProjectRuntimeLogsContent renderRowAction={renderRowAction} />\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ObservabilityProjectRuntimeLogsPage.esm.js","sources":["../../../src/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { Box, Typography, Button } from '@material-ui/core';\nimport { Progress } from '@backstage/core-components';\nimport { Alert } from '@material-ui/lab';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport { LogsFilter } from './LogsFilter';\nimport { LogsTable } from './LogsTable';\nimport { LogsActions } from './LogsActions';\nimport {\n useGetEnvironmentsByNamespace,\n useGetComponentsByProject,\n useProjectRuntimeLogs,\n useUrlFiltersForRuntimeLogs,\n} from '../../hooks';\nimport {\n useInfiniteScroll,\n useLogsPermission,\n ForbiddenState,\n} from '@openchoreo/backstage-plugin-react';\nimport { NoEnvironmentsEmptyState } from '../common';\nimport { useRuntimeLogsStyles } from './styles';\nimport { LogEntryField } from './types';\nimport type { RenderLogRowAction } from './LogEntry';\n\nexport interface ObservabilityProjectRuntimeLogsPageProps {\n renderRowAction?: RenderLogRowAction;\n}\n\nconst ObservabilityProjectRuntimeLogsContent = ({\n renderRowAction,\n}: ObservabilityProjectRuntimeLogsPageProps) => {\n const classes = useRuntimeLogsStyles();\n const { entity } = useEntity();\n\n const namespace =\n entity.metadata.annotations?.[CHOREO_ANNOTATIONS.NAMESPACE] || '';\n const projectName = entity.metadata.name || '';\n\n const {\n environments,\n loading: environmentsLoading,\n error: environmentsError,\n } = useGetEnvironmentsByNamespace(namespace, projectName);\n\n const {\n components,\n loading: componentsLoading,\n error: componentsError,\n } = useGetComponentsByProject(entity);\n\n const { filters, updateFilters } = useUrlFiltersForRuntimeLogs({\n environments,\n });\n\n const selectedEnvironment = environments.find(\n env => env.name === filters.environment,\n );\n const tableSelectedFields = useMemo(() => {\n const withoutComponent = filters.selectedFields.filter(\n field => field !== LogEntryField.ComponentName,\n );\n const logIndex = withoutComponent.indexOf(LogEntryField.Log);\n\n if (logIndex === -1) {\n return [...withoutComponent, LogEntryField.ComponentName];\n }\n\n return [\n ...withoutComponent.slice(0, logIndex),\n LogEntryField.ComponentName,\n ...withoutComponent.slice(logIndex),\n ];\n }, [filters.selectedFields]);\n\n const [lastUpdated, setLastUpdated] = useState<Date>(new Date());\n\n const {\n logs,\n loading: logsLoading,\n error: logsError,\n totalCount,\n hasMore,\n fetchLogs,\n loadMore,\n refresh,\n clearLogs,\n } = useProjectRuntimeLogs(filters, entity, {\n environmentName: filters.environment,\n namespaceName: namespace,\n projectName,\n limit: 50,\n });\n\n const { loadingRef } = useInfiniteScroll(loadMore, hasMore, logsLoading);\n\n const previousFiltersRef = useRef<{\n environment: string;\n logLevel: string[];\n timeRange: string;\n customStartTime?: string;\n customEndTime?: string;\n searchQuery?: string;\n sortOrder?: 'asc' | 'desc';\n components: string[];\n } | null>(null);\n\n useEffect(() => {\n const currentFilters = {\n environment: filters.environment,\n logLevel: filters.logLevel,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n searchQuery: filters.searchQuery,\n sortOrder: filters.sortOrder,\n components: filters.components || [],\n };\n\n const filtersChanged =\n previousFiltersRef.current === null ||\n JSON.stringify(previousFiltersRef.current) !==\n JSON.stringify(currentFilters);\n\n if (\n filters.environment &&\n selectedEnvironment &&\n namespace &&\n projectName &&\n filtersChanged\n ) {\n if (filters.logLevel.length === 0) {\n clearLogs();\n } else {\n fetchLogs(true);\n setLastUpdated(new Date());\n }\n previousFiltersRef.current = currentFilters;\n }\n }, [\n filters.environment,\n filters.logLevel,\n filters.timeRange,\n filters.customStartTime,\n filters.customEndTime,\n filters.searchQuery,\n filters.sortOrder,\n filters.components,\n fetchLogs,\n clearLogs,\n selectedEnvironment,\n namespace,\n projectName,\n ]);\n\n useEffect(() => {\n if (!logsLoading) {\n setLastUpdated(new Date());\n }\n }, [logsLoading]);\n\n const handleRefresh = () => {\n refresh();\n setLastUpdated(new Date());\n };\n\n const handleFiltersChange = (newFilters: Partial<typeof filters>) => {\n updateFilters(newFilters);\n };\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 project in the current environment. Enable observability to view runtime logs.'\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 if (environmentsError) {\n return <Box>{renderError(environmentsError)}</Box>;\n }\n\n if (componentsError) {\n return <Box>{renderError(componentsError)}</Box>;\n }\n\n if (!environmentsLoading && environments.length === 0) {\n return (\n <Box>\n <NoEnvironmentsEmptyState feature=\"logs\" />\n </Box>\n );\n }\n\n return (\n <Box>\n <LogsFilter\n filters={filters}\n onFiltersChange={handleFiltersChange}\n environments={environments}\n environmentsLoading={environmentsLoading}\n components={components}\n componentsLoading={componentsLoading}\n disabled={logsLoading}\n />\n\n {logsError && renderError(logsError)}\n\n {filters.environment && (\n <>\n <LogsActions\n totalCount={totalCount}\n disabled={logsLoading || !filters.environment}\n onRefresh={handleRefresh}\n filters={filters}\n onFiltersChange={handleFiltersChange}\n lastUpdated={lastUpdated}\n />\n\n <LogsTable\n selectedFields={tableSelectedFields}\n logs={logs}\n loading={logsLoading}\n hasMore={hasMore}\n loadingRef={loadingRef}\n environmentName={\n selectedEnvironment?.displayName || selectedEnvironment?.name\n }\n projectName={projectName}\n renderRowAction={renderRowAction}\n />\n </>\n )}\n </Box>\n );\n};\n\nexport const ObservabilityProjectRuntimeLogsPage = ({\n renderRowAction,\n}: ObservabilityProjectRuntimeLogsPageProps) => {\n const {\n canViewLogs,\n loading: permissionLoading,\n deniedTooltip,\n permissionName,\n } = useLogsPermission();\n\n if (permissionLoading) {\n return <Progress />;\n }\n\n if (!canViewLogs) {\n return (\n <ForbiddenState\n message={deniedTooltip}\n permissionName={permissionName}\n variant=\"fullpage\"\n />\n );\n }\n\n return (\n <ObservabilityProjectRuntimeLogsContent renderRowAction={renderRowAction} />\n );\n};\n"],"names":[],"mappings":"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"}
@@ -20,6 +20,7 @@ import { LOG_LEVELS } from './types.esm.js';
20
20
  import { useUrlFiltersForRuntimeLogs } from '../../hooks/useUrlFiltersForRuntimeLogs.esm.js';
21
21
  import '../../api/RCAAgentApi.esm.js';
22
22
  import '../../api/FinOpsAgentApi.esm.js';
23
+ import { NoEnvironmentsEmptyState } from '../common/NoEnvironmentsEmptyState.esm.js';
23
24
  import { useRuntimeLogsStyles } from './styles.esm.js';
24
25
 
25
26
  const ObservabilityRuntimeLogsContent = ({
@@ -143,6 +144,9 @@ const ObservabilityRuntimeLogsContent = ({
143
144
  if (environmentsError) {
144
145
  return /* @__PURE__ */ jsx(Box, { children: renderError(environmentsError) });
145
146
  }
147
+ if (!environmentsLoading && environments.length === 0) {
148
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(NoEnvironmentsEmptyState, { feature: "logs" }) });
149
+ }
146
150
  return /* @__PURE__ */ jsxs(Box, { children: [
147
151
  /* @__PURE__ */ jsx(
148
152
  LogsFilter,
@@ -155,7 +159,6 @@ const ObservabilityRuntimeLogsContent = ({
155
159
  }
156
160
  ),
157
161
  logsError && renderError(logsError),
158
- !filters.environment && !environmentsLoading && environments.length === 0 && /* @__PURE__ */ jsx(Alert, { severity: "info", className: classes.errorContainer, children: /* @__PURE__ */ jsx(Typography, { variant: "body1", children: "No environments found. Make sure your component is properly configured." }) }),
159
162
  filters.environment && !envPermissionLoading && !canViewLogsForEnv && /* @__PURE__ */ jsx(
160
163
  ForbiddenState,
161
164
  {
@@ -1 +1 @@
1
- {"version":3,"file":"ObservabilityRuntimeLogsPage.esm.js","sources":["../../../src/components/RuntimeLogs/ObservabilityRuntimeLogsPage.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { Box, Typography, Button } from '@material-ui/core';\nimport { Progress } from '@backstage/core-components';\nimport { Alert } from '@material-ui/lab';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport { LogsFilter } from './LogsFilter';\nimport { LogsTable } from './LogsTable';\nimport { LogsActions } from './LogsActions';\nimport {\n useRuntimeLogs,\n useGetNamespaceAndProjectByEntity,\n useGetEnvironmentsByNamespace,\n useUrlFiltersForRuntimeLogs,\n} from '../../hooks';\nimport {\n useInfiniteScroll,\n useLogsPermission,\n ForbiddenState,\n} from '@openchoreo/backstage-plugin-react';\nimport { useRuntimeLogsStyles } from './styles';\nimport { LOG_LEVELS } from './types';\nimport type { RenderLogRowAction } from './LogEntry';\n\nexport interface ObservabilityRuntimeLogsPageProps {\n renderRowAction?: RenderLogRowAction;\n}\n\nconst ObservabilityRuntimeLogsContent = ({\n renderRowAction,\n}: ObservabilityRuntimeLogsPageProps) => {\n const classes = useRuntimeLogsStyles();\n const { entity } = useEntity();\n\n // Get namespace and project names from entity\n const { namespace, project } = useGetNamespaceAndProjectByEntity(entity);\n\n // Fetch environments from observability backend\n const {\n environments,\n loading: environmentsLoading,\n error: environmentsError,\n } = useGetEnvironmentsByNamespace(namespace, project);\n\n const { filters, updateFilters } = useUrlFiltersForRuntimeLogs({\n environments,\n });\n\n const selectedEnvironment = environments.find(\n env => env.name === filters.environment,\n );\n\n // Per-environment permission check: honors ABAC `resource.environment` CEL\n // constraints (openchoreo#3408). Page-level gate below only verifies the\n // user can view logs *somewhere* — this one gates the actual env content.\n const {\n canViewLogs: canViewLogsForEnv,\n loading: envPermissionLoading,\n deniedTooltip: envPermissionDenied,\n permissionName: envPermissionName,\n } = useLogsPermission(selectedEnvironment?.name);\n\n // Get component name from entity annotations\n const componentName =\n entity.metadata.annotations?.[CHOREO_ANNOTATIONS.COMPONENT];\n\n // Track last updated time\n const [lastUpdated, setLastUpdated] = useState<Date>(new Date());\n\n const allLogLevelsSelected = filters.logLevel.length === LOG_LEVELS.length;\n const noLogLevelSelected = filters.logLevel.length === 0;\n\n const {\n logs,\n loading: logsLoading,\n error: logsError,\n totalCount,\n hasMore,\n fetchLogs,\n loadMore,\n refresh,\n clearLogs,\n } = useRuntimeLogs(entity, namespace || '', project || '', {\n environment: filters.environment,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n logLevels: allLogLevelsSelected ? undefined : filters.logLevel,\n limit: 50,\n searchQuery: filters.searchQuery,\n sortOrder: filters.sortOrder || 'asc',\n isLive: filters.isLive && !noLogLevelSelected,\n });\n\n const { loadingRef } = useInfiniteScroll(loadMore, hasMore, logsLoading);\n\n // Track previous filter values to detect changes\n // Initialize with null to ensure initial fetch happens when all conditions are ready\n const previousFiltersRef = useRef<{\n environment: string;\n logLevel: string[];\n timeRange: string;\n customStartTime?: string;\n customEndTime?: string;\n searchQuery?: string;\n sortOrder?: 'asc' | 'desc';\n } | null>(null);\n\n // Note: Auto-selection of first environment is handled by useUrlFilters hook\n\n // Fetch logs when filters change or when component/project IDs become available\n useEffect(() => {\n const currentFilters = {\n environment: filters.environment,\n logLevel: filters.logLevel,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n searchQuery: filters.searchQuery,\n sortOrder: filters.sortOrder,\n };\n\n // Only fetch if filters changed (null means first load)\n const filtersChanged =\n previousFiltersRef.current === null ||\n JSON.stringify(previousFiltersRef.current) !==\n JSON.stringify(currentFilters);\n\n if (\n filters.environment &&\n selectedEnvironment &&\n namespace &&\n project &&\n canViewLogsForEnv &&\n filtersChanged\n ) {\n if (filters.logLevel.length === 0) {\n clearLogs();\n } else {\n fetchLogs(true);\n setLastUpdated(new Date());\n }\n previousFiltersRef.current = currentFilters;\n }\n }, [\n filters.environment,\n filters.logLevel,\n filters.timeRange,\n filters.customStartTime,\n filters.customEndTime,\n filters.searchQuery,\n filters.sortOrder,\n fetchLogs,\n clearLogs,\n selectedEnvironment,\n namespace,\n project,\n canViewLogsForEnv,\n envPermissionLoading,\n ]);\n\n // Update lastUpdated when logs are refreshed\n useEffect(() => {\n if (!logsLoading) {\n setLastUpdated(new Date());\n }\n }, [logsLoading]);\n\n const handleRefresh = () => {\n if (noLogLevelSelected) {\n clearLogs();\n return;\n }\n refresh();\n setLastUpdated(new Date());\n };\n\n const handleFiltersChange = (newFilters: Partial<typeof filters>) => {\n updateFilters(newFilters);\n };\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 runtime logs.'\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 if (environmentsError) {\n return <Box>{renderError(environmentsError)}</Box>;\n }\n\n return (\n <Box>\n <LogsFilter\n filters={filters}\n onFiltersChange={handleFiltersChange}\n environments={environments}\n environmentsLoading={environmentsLoading}\n disabled={logsLoading}\n />\n\n {logsError && renderError(logsError)}\n\n {!filters.environment &&\n !environmentsLoading &&\n environments.length === 0 && (\n <Alert severity=\"info\" className={classes.errorContainer}>\n <Typography variant=\"body1\">\n No environments found. Make sure your component is properly\n configured.\n </Typography>\n </Alert>\n )}\n\n {filters.environment && !envPermissionLoading && !canViewLogsForEnv && (\n <ForbiddenState\n message={envPermissionDenied}\n permissionName={envPermissionName}\n variant=\"compact\"\n />\n )}\n\n {filters.environment && canViewLogsForEnv && (\n <>\n <LogsActions\n totalCount={totalCount}\n disabled={logsLoading || !filters.environment}\n onRefresh={handleRefresh}\n filters={filters}\n onFiltersChange={handleFiltersChange}\n lastUpdated={lastUpdated}\n />\n\n <LogsTable\n selectedFields={filters.selectedFields}\n logs={logs}\n loading={logsLoading}\n hasMore={hasMore}\n loadingRef={loadingRef}\n environmentName={\n selectedEnvironment?.displayName || selectedEnvironment?.name\n }\n projectName={project}\n componentName={componentName}\n renderRowAction={renderRowAction}\n />\n </>\n )}\n </Box>\n );\n};\n\nexport const ObservabilityRuntimeLogsPage = ({\n renderRowAction,\n}: ObservabilityRuntimeLogsPageProps) => {\n const {\n canViewLogs,\n loading: permissionLoading,\n deniedTooltip,\n permissionName,\n } = useLogsPermission();\n\n if (permissionLoading) {\n return <Progress />;\n }\n\n if (!canViewLogs) {\n return (\n <ForbiddenState\n message={deniedTooltip}\n permissionName={permissionName}\n variant=\"fullpage\"\n />\n );\n }\n\n return <ObservabilityRuntimeLogsContent renderRowAction={renderRowAction} />;\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ObservabilityRuntimeLogsPage.esm.js","sources":["../../../src/components/RuntimeLogs/ObservabilityRuntimeLogsPage.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { Box, Typography, Button } from '@material-ui/core';\nimport { Progress } from '@backstage/core-components';\nimport { Alert } from '@material-ui/lab';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport { LogsFilter } from './LogsFilter';\nimport { LogsTable } from './LogsTable';\nimport { LogsActions } from './LogsActions';\nimport {\n useRuntimeLogs,\n useGetNamespaceAndProjectByEntity,\n useGetEnvironmentsByNamespace,\n useUrlFiltersForRuntimeLogs,\n} from '../../hooks';\nimport {\n useInfiniteScroll,\n useLogsPermission,\n ForbiddenState,\n} from '@openchoreo/backstage-plugin-react';\nimport { NoEnvironmentsEmptyState } from '../common';\nimport { useRuntimeLogsStyles } from './styles';\nimport { LOG_LEVELS } from './types';\nimport type { RenderLogRowAction } from './LogEntry';\n\nexport interface ObservabilityRuntimeLogsPageProps {\n renderRowAction?: RenderLogRowAction;\n}\n\nconst ObservabilityRuntimeLogsContent = ({\n renderRowAction,\n}: ObservabilityRuntimeLogsPageProps) => {\n const classes = useRuntimeLogsStyles();\n const { entity } = useEntity();\n\n // Get namespace and project names from entity\n const { namespace, project } = useGetNamespaceAndProjectByEntity(entity);\n\n // Fetch environments from observability backend\n const {\n environments,\n loading: environmentsLoading,\n error: environmentsError,\n } = useGetEnvironmentsByNamespace(namespace, project);\n\n const { filters, updateFilters } = useUrlFiltersForRuntimeLogs({\n environments,\n });\n\n const selectedEnvironment = environments.find(\n env => env.name === filters.environment,\n );\n\n // Per-environment permission check: honors ABAC `resource.environment` CEL\n // constraints (openchoreo#3408). Page-level gate below only verifies the\n // user can view logs *somewhere* — this one gates the actual env content.\n const {\n canViewLogs: canViewLogsForEnv,\n loading: envPermissionLoading,\n deniedTooltip: envPermissionDenied,\n permissionName: envPermissionName,\n } = useLogsPermission(selectedEnvironment?.name);\n\n // Get component name from entity annotations\n const componentName =\n entity.metadata.annotations?.[CHOREO_ANNOTATIONS.COMPONENT];\n\n // Track last updated time\n const [lastUpdated, setLastUpdated] = useState<Date>(new Date());\n\n const allLogLevelsSelected = filters.logLevel.length === LOG_LEVELS.length;\n const noLogLevelSelected = filters.logLevel.length === 0;\n\n const {\n logs,\n loading: logsLoading,\n error: logsError,\n totalCount,\n hasMore,\n fetchLogs,\n loadMore,\n refresh,\n clearLogs,\n } = useRuntimeLogs(entity, namespace || '', project || '', {\n environment: filters.environment,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n logLevels: allLogLevelsSelected ? undefined : filters.logLevel,\n limit: 50,\n searchQuery: filters.searchQuery,\n sortOrder: filters.sortOrder || 'asc',\n isLive: filters.isLive && !noLogLevelSelected,\n });\n\n const { loadingRef } = useInfiniteScroll(loadMore, hasMore, logsLoading);\n\n // Track previous filter values to detect changes\n // Initialize with null to ensure initial fetch happens when all conditions are ready\n const previousFiltersRef = useRef<{\n environment: string;\n logLevel: string[];\n timeRange: string;\n customStartTime?: string;\n customEndTime?: string;\n searchQuery?: string;\n sortOrder?: 'asc' | 'desc';\n } | null>(null);\n\n // Note: Auto-selection of first environment is handled by useUrlFilters hook\n\n // Fetch logs when filters change or when component/project IDs become available\n useEffect(() => {\n const currentFilters = {\n environment: filters.environment,\n logLevel: filters.logLevel,\n timeRange: filters.timeRange,\n customStartTime: filters.customStartTime,\n customEndTime: filters.customEndTime,\n searchQuery: filters.searchQuery,\n sortOrder: filters.sortOrder,\n };\n\n // Only fetch if filters changed (null means first load)\n const filtersChanged =\n previousFiltersRef.current === null ||\n JSON.stringify(previousFiltersRef.current) !==\n JSON.stringify(currentFilters);\n\n if (\n filters.environment &&\n selectedEnvironment &&\n namespace &&\n project &&\n canViewLogsForEnv &&\n filtersChanged\n ) {\n if (filters.logLevel.length === 0) {\n clearLogs();\n } else {\n fetchLogs(true);\n setLastUpdated(new Date());\n }\n previousFiltersRef.current = currentFilters;\n }\n }, [\n filters.environment,\n filters.logLevel,\n filters.timeRange,\n filters.customStartTime,\n filters.customEndTime,\n filters.searchQuery,\n filters.sortOrder,\n fetchLogs,\n clearLogs,\n selectedEnvironment,\n namespace,\n project,\n canViewLogsForEnv,\n envPermissionLoading,\n ]);\n\n // Update lastUpdated when logs are refreshed\n useEffect(() => {\n if (!logsLoading) {\n setLastUpdated(new Date());\n }\n }, [logsLoading]);\n\n const handleRefresh = () => {\n if (noLogLevelSelected) {\n clearLogs();\n return;\n }\n refresh();\n setLastUpdated(new Date());\n };\n\n const handleFiltersChange = (newFilters: Partial<typeof filters>) => {\n updateFilters(newFilters);\n };\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 runtime logs.'\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 if (environmentsError) {\n return <Box>{renderError(environmentsError)}</Box>;\n }\n\n if (!environmentsLoading && environments.length === 0) {\n return (\n <Box>\n <NoEnvironmentsEmptyState feature=\"logs\" />\n </Box>\n );\n }\n\n return (\n <Box>\n <LogsFilter\n filters={filters}\n onFiltersChange={handleFiltersChange}\n environments={environments}\n environmentsLoading={environmentsLoading}\n disabled={logsLoading}\n />\n\n {logsError && renderError(logsError)}\n\n {filters.environment && !envPermissionLoading && !canViewLogsForEnv && (\n <ForbiddenState\n message={envPermissionDenied}\n permissionName={envPermissionName}\n variant=\"compact\"\n />\n )}\n\n {filters.environment && canViewLogsForEnv && (\n <>\n <LogsActions\n totalCount={totalCount}\n disabled={logsLoading || !filters.environment}\n onRefresh={handleRefresh}\n filters={filters}\n onFiltersChange={handleFiltersChange}\n lastUpdated={lastUpdated}\n />\n\n <LogsTable\n selectedFields={filters.selectedFields}\n logs={logs}\n loading={logsLoading}\n hasMore={hasMore}\n loadingRef={loadingRef}\n environmentName={\n selectedEnvironment?.displayName || selectedEnvironment?.name\n }\n projectName={project}\n componentName={componentName}\n renderRowAction={renderRowAction}\n />\n </>\n )}\n </Box>\n );\n};\n\nexport const ObservabilityRuntimeLogsPage = ({\n renderRowAction,\n}: ObservabilityRuntimeLogsPageProps) => {\n const {\n canViewLogs,\n loading: permissionLoading,\n deniedTooltip,\n permissionName,\n } = useLogsPermission();\n\n if (permissionLoading) {\n return <Progress />;\n }\n\n if (!canViewLogs) {\n return (\n <ForbiddenState\n message={deniedTooltip}\n permissionName={permissionName}\n variant=\"fullpage\"\n />\n );\n }\n\n return <ObservabilityRuntimeLogsContent renderRowAction={renderRowAction} />;\n};\n"],"names":[],"mappings":"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"}
@@ -21,6 +21,7 @@ import '../../api/RCAAgentApi.esm.js';
21
21
  import '../../api/FinOpsAgentApi.esm.js';
22
22
  import { Progress } from '@backstage/core-components';
23
23
  import { Alert } from '@material-ui/lab';
24
+ import { NoEnvironmentsEmptyState } from '../common/NoEnvironmentsEmptyState.esm.js';
24
25
 
25
26
  const ObservabilityTracesContent = () => {
26
27
  const { entity } = useEntity();
@@ -86,6 +87,9 @@ const ObservabilityTracesContent = () => {
86
87
  if (environmentsError) {
87
88
  return /* @__PURE__ */ jsx(Fragment, {});
88
89
  }
90
+ if (!environmentsLoading && environments.length === 0) {
91
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(NoEnvironmentsEmptyState, { feature: "traces" }) });
92
+ }
89
93
  const renderError = (error) => {
90
94
  const isObservabilityDisabled = error.includes(
91
95
  "Observability is not enabled"
@@ -1 +1 @@
1
- {"version":3,"file":"ObservabilityTracesPage.esm.js","sources":["../../../src/components/Traces/ObservabilityTracesPage.tsx"],"sourcesContent":["import { useCallback } from 'react';\nimport { Box, Button, Typography } from '@material-ui/core';\nimport { TracesFilters } from './TracesFilters';\nimport { TracesActions } from './TracesActions';\nimport { TracesTable } from './TracesTable';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport {\n useUrlFilters,\n useTraces,\n useGetEnvironmentsByNamespace,\n useGetComponentsByProject,\n} from '../../hooks';\nimport { useTraceSpans } from '../../hooks/useTraceSpans';\nimport { useSpanDetails } from '../../hooks/useSpanDetails';\nimport type { Filters } from '../../types';\nimport { Progress } from '@backstage/core-components';\nimport { Alert } from '@material-ui/lab';\nimport {\n useTracesPermission,\n ForbiddenState,\n calculateTimeRange,\n} from '@openchoreo/backstage-plugin-react';\n\nconst ObservabilityTracesContent = () => {\n const { entity } = useEntity();\n const namespace =\n entity.metadata.annotations?.[CHOREO_ANNOTATIONS.NAMESPACE] ?? '';\n const projectName = entity.metadata.name as string;\n\n const {\n environments,\n loading: environmentsLoading,\n error: environmentsError,\n } = useGetEnvironmentsByNamespace(namespace, projectName);\n const {\n components,\n loading: componentsLoading,\n error: componentsError,\n } = useGetComponentsByProject(entity);\n\n const { filters, updateFilters } = useUrlFilters({\n environments,\n });\n\n // Per-environment permission (ABAC `resource.environment`) — gates the\n // traces content once an environment is selected. See openchoreo#3408.\n const {\n canViewTraces: canViewTracesForEnv,\n loading: envPermissionLoading,\n deniedTooltip: envPermissionDenied,\n permissionName: envPermissionName,\n } = useTracesPermission(filters.environment?.name);\n\n // Don't fire the backend query until the per-env permission resolves to\n // allow — otherwise a denied user produces a 403 we'd have to suppress.\n // useTraces guards on filters.environment internally, so clearing it\n // turns the hook into a no-op while keeping the same hook-call order.\n const tracesFilters: Filters =\n canViewTracesForEnv && !envPermissionLoading\n ? filters\n : ({ ...filters, environment: undefined } as unknown as Filters);\n const {\n traces,\n total,\n loading: tracesLoading,\n error: tracesError,\n refresh,\n } = useTraces(tracesFilters, entity);\n\n // Determine which component name to pass for span queries\n // (mirrors what useTraces does — single selection passes the name)\n const selectedComponents = filters.components ?? [];\n const componentName =\n selectedComponents.length === 1 ? selectedComponents[0] : undefined;\n\n const { startTime, endTime } = filters.timeRange\n ? calculateTimeRange(filters.timeRange, {\n startTime: filters.customStartTime,\n endTime: filters.customEndTime,\n })\n : { startTime: undefined, endTime: undefined };\n\n const traceSpans = useTraceSpans({\n namespaceName: namespace,\n projectName,\n environmentName: filters.environment?.name ?? '',\n componentName,\n startTime,\n endTime,\n });\n\n const spanDetails = useSpanDetails({\n namespaceName: namespace,\n environmentName: filters.environment?.name ?? '',\n });\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 (componentsError) {\n return <></>;\n }\n\n if (environmentsError) {\n return <></>;\n }\n\n const renderError = (error: string) => {\n const isObservabilityDisabled = error.includes(\n 'Observability is not enabled',\n );\n\n return (\n <Box mt={2} mb={2}>\n <Alert severity={isObservabilityDisabled ? 'info' : 'error'}>\n <Typography variant=\"body1\">\n {isObservabilityDisabled\n ? 'Observability is not enabled for this project in the current environment. Enable observability to view traces.'\n : error}\n </Typography>\n {!isObservabilityDisabled && (\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 {tracesLoading && <Progress />}\n\n {!tracesLoading && (\n <>\n <TracesFilters\n filters={filters}\n onFiltersChange={handleFiltersChange}\n environments={environments}\n environmentsLoading={environmentsLoading}\n components={components}\n componentsLoading={componentsLoading}\n />\n\n {filters.environment &&\n !envPermissionLoading &&\n !canViewTracesForEnv && (\n <ForbiddenState\n message={envPermissionDenied}\n permissionName={envPermissionName}\n variant=\"compact\"\n />\n )}\n\n {(canViewTracesForEnv || envPermissionLoading) &&\n tracesError &&\n renderError(tracesError)}\n\n {(canViewTracesForEnv || envPermissionLoading) && (\n <TracesActions\n totalCount={total}\n disabled={tracesLoading}\n onRefresh={handleRefresh}\n />\n )}\n\n {(canViewTracesForEnv || envPermissionLoading) && (\n <TracesTable\n traces={traces}\n traceSpans={traceSpans}\n spanDetails={spanDetails}\n loading={tracesLoading || envPermissionLoading}\n />\n )}\n </>\n )}\n </Box>\n );\n};\n\nexport const ObservabilityTracesPage = () => {\n const {\n canViewTraces,\n loading: permissionLoading,\n deniedTooltip,\n permissionName,\n } = useTracesPermission();\n\n if (permissionLoading) {\n return <Progress />;\n }\n\n if (!canViewTraces) {\n return (\n <ForbiddenState\n message={deniedTooltip}\n permissionName={permissionName}\n variant=\"fullpage\"\n />\n );\n }\n\n return <ObservabilityTracesContent />;\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ObservabilityTracesPage.esm.js","sources":["../../../src/components/Traces/ObservabilityTracesPage.tsx"],"sourcesContent":["import { useCallback } from 'react';\nimport { Box, Button, Typography } from '@material-ui/core';\nimport { TracesFilters } from './TracesFilters';\nimport { TracesActions } from './TracesActions';\nimport { TracesTable } from './TracesTable';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { CHOREO_ANNOTATIONS } from '@openchoreo/backstage-plugin-common';\nimport {\n useUrlFilters,\n useTraces,\n useGetEnvironmentsByNamespace,\n useGetComponentsByProject,\n} from '../../hooks';\nimport { useTraceSpans } from '../../hooks/useTraceSpans';\nimport { useSpanDetails } from '../../hooks/useSpanDetails';\nimport type { Filters } from '../../types';\nimport { Progress } from '@backstage/core-components';\nimport { Alert } from '@material-ui/lab';\nimport {\n useTracesPermission,\n ForbiddenState,\n calculateTimeRange,\n} from '@openchoreo/backstage-plugin-react';\nimport { NoEnvironmentsEmptyState } from '../common';\n\nconst ObservabilityTracesContent = () => {\n const { entity } = useEntity();\n const namespace =\n entity.metadata.annotations?.[CHOREO_ANNOTATIONS.NAMESPACE] ?? '';\n const projectName = entity.metadata.name as string;\n\n const {\n environments,\n loading: environmentsLoading,\n error: environmentsError,\n } = useGetEnvironmentsByNamespace(namespace, projectName);\n const {\n components,\n loading: componentsLoading,\n error: componentsError,\n } = useGetComponentsByProject(entity);\n\n const { filters, updateFilters } = useUrlFilters({\n environments,\n });\n\n // Per-environment permission (ABAC `resource.environment`) — gates the\n // traces content once an environment is selected. See openchoreo#3408.\n const {\n canViewTraces: canViewTracesForEnv,\n loading: envPermissionLoading,\n deniedTooltip: envPermissionDenied,\n permissionName: envPermissionName,\n } = useTracesPermission(filters.environment?.name);\n\n // Don't fire the backend query until the per-env permission resolves to\n // allow — otherwise a denied user produces a 403 we'd have to suppress.\n // useTraces guards on filters.environment internally, so clearing it\n // turns the hook into a no-op while keeping the same hook-call order.\n const tracesFilters: Filters =\n canViewTracesForEnv && !envPermissionLoading\n ? filters\n : ({ ...filters, environment: undefined } as unknown as Filters);\n const {\n traces,\n total,\n loading: tracesLoading,\n error: tracesError,\n refresh,\n } = useTraces(tracesFilters, entity);\n\n // Determine which component name to pass for span queries\n // (mirrors what useTraces does — single selection passes the name)\n const selectedComponents = filters.components ?? [];\n const componentName =\n selectedComponents.length === 1 ? selectedComponents[0] : undefined;\n\n const { startTime, endTime } = filters.timeRange\n ? calculateTimeRange(filters.timeRange, {\n startTime: filters.customStartTime,\n endTime: filters.customEndTime,\n })\n : { startTime: undefined, endTime: undefined };\n\n const traceSpans = useTraceSpans({\n namespaceName: namespace,\n projectName,\n environmentName: filters.environment?.name ?? '',\n componentName,\n startTime,\n endTime,\n });\n\n const spanDetails = useSpanDetails({\n namespaceName: namespace,\n environmentName: filters.environment?.name ?? '',\n });\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 (componentsError) {\n return <></>;\n }\n\n if (environmentsError) {\n return <></>;\n }\n\n if (!environmentsLoading && environments.length === 0) {\n return (\n <Box>\n <NoEnvironmentsEmptyState feature=\"traces\" />\n </Box>\n );\n }\n\n const renderError = (error: string) => {\n const isObservabilityDisabled = error.includes(\n 'Observability is not enabled',\n );\n\n return (\n <Box mt={2} mb={2}>\n <Alert severity={isObservabilityDisabled ? 'info' : 'error'}>\n <Typography variant=\"body1\">\n {isObservabilityDisabled\n ? 'Observability is not enabled for this project in the current environment. Enable observability to view traces.'\n : error}\n </Typography>\n {!isObservabilityDisabled && (\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 {tracesLoading && <Progress />}\n\n {!tracesLoading && (\n <>\n <TracesFilters\n filters={filters}\n onFiltersChange={handleFiltersChange}\n environments={environments}\n environmentsLoading={environmentsLoading}\n components={components}\n componentsLoading={componentsLoading}\n />\n\n {filters.environment &&\n !envPermissionLoading &&\n !canViewTracesForEnv && (\n <ForbiddenState\n message={envPermissionDenied}\n permissionName={envPermissionName}\n variant=\"compact\"\n />\n )}\n\n {(canViewTracesForEnv || envPermissionLoading) &&\n tracesError &&\n renderError(tracesError)}\n\n {(canViewTracesForEnv || envPermissionLoading) && (\n <TracesActions\n totalCount={total}\n disabled={tracesLoading}\n onRefresh={handleRefresh}\n />\n )}\n\n {(canViewTracesForEnv || envPermissionLoading) && (\n <TracesTable\n traces={traces}\n traceSpans={traceSpans}\n spanDetails={spanDetails}\n loading={tracesLoading || envPermissionLoading}\n />\n )}\n </>\n )}\n </Box>\n );\n};\n\nexport const ObservabilityTracesPage = () => {\n const {\n canViewTraces,\n loading: permissionLoading,\n deniedTooltip,\n permissionName,\n } = useTracesPermission();\n\n if (permissionLoading) {\n return <Progress />;\n }\n\n if (!canViewTraces) {\n return (\n <ForbiddenState\n message={deniedTooltip}\n permissionName={permissionName}\n variant=\"fullpage\"\n />\n );\n }\n\n return <ObservabilityTracesContent />;\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { EmptyState } from '@backstage/core-components';
3
+
4
+ const NoEnvironmentsEmptyState = ({
5
+ feature
6
+ }) => /* @__PURE__ */ jsx(
7
+ EmptyState,
8
+ {
9
+ missing: "content",
10
+ title: `No environments available to view ${feature}`,
11
+ description: "This project's deployment pipeline has no environments configured. Review the deployment pipeline or contact your administrator."
12
+ }
13
+ );
14
+
15
+ export { NoEnvironmentsEmptyState };
16
+ //# sourceMappingURL=NoEnvironmentsEmptyState.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NoEnvironmentsEmptyState.esm.js","sources":["../../../src/components/common/NoEnvironmentsEmptyState.tsx"],"sourcesContent":["import { EmptyState } from '@backstage/core-components';\n\nexport interface NoEnvironmentsEmptyStateProps {\n /**\n * What the page shows (e.g. `logs`, `metrics`), used to make the title\n * specific: \"No environments available to view {feature}\".\n */\n feature: string;\n}\n\n/**\n * Empty state shown when a project's deployment pipeline has no environments\n * configured, so observability pages explain what happened instead of\n * rendering empty filters. Uses the standard Backstage `EmptyState` to match\n * the Deploy tab.\n */\nexport const NoEnvironmentsEmptyState = ({\n feature,\n}: NoEnvironmentsEmptyStateProps) => (\n <EmptyState\n missing=\"content\"\n title={`No environments available to view ${feature}`}\n description=\"This project's deployment pipeline has no environments configured. Review the deployment pipeline or contact your administrator.\"\n />\n);\n"],"names":[],"mappings":";;;AAgBO,MAAM,2BAA2B,CAAC;AAAA,EACvC;AACF,CAAA,qBACE,GAAA;AAAA,EAAC,UAAA;AAAA,EAAA;AAAA,IACC,OAAA,EAAQ,SAAA;AAAA,IACR,KAAA,EAAO,qCAAqC,OAAO,CAAA,CAAA;AAAA,IACnD,WAAA,EAAY;AAAA;AACd;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openchoreo/backstage-plugin-openchoreo-observability",
3
- "version": "1.1.1",
3
+ "version": "1.1.5",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.esm.js",
6
6
  "types": "dist/index.d.ts",
@@ -42,9 +42,9 @@
42
42
  "@material-ui/icons": "4.11.3",
43
43
  "@material-ui/lab": "4.0.0-alpha.61",
44
44
  "@material-ui/pickers": "3.3.11",
45
- "@openchoreo/backstage-design-system": "^1.1.1",
46
- "@openchoreo/backstage-plugin-common": "^1.1.1",
47
- "@openchoreo/backstage-plugin-react": "^1.1.1",
45
+ "@openchoreo/backstage-design-system": "^1.1.5",
46
+ "@openchoreo/backstage-plugin-common": "^1.1.5",
47
+ "@openchoreo/backstage-plugin-react": "^1.1.5",
48
48
  "date-fns": "2.30.0",
49
49
  "react-markdown": "^9.0.1",
50
50
  "react-use": "17.6.0",