import {RefreshCw} from 'lucide-react' import {useQuery} from '@tanstack/react-query' import {useMemo, useState} from 'react' import {getMetricSeries, listMetrics} from '@/api/cloudProxyClient' import {MetricChart} from '@/components/MetricChart' import {RANGES} from '@/lib/metricChart' import {useAccountId} from '@/lib/accountStore' import type {MetricSummary} from '@/types/resource' const STATISTICS = ['Average', 'Sum', 'Minimum', 'Maximum', 'SampleCount'] export function CloudWatchMetricsExplorer({runtimeReachable}: {runtimeReachable: boolean}) { const accountId = useAccountId() const [namespace, setNamespace] = useState('') const [search, setSearch] = useState('') const [selected, setSelected] = useState(null) const [statistic, setStatistic] = useState('Average') const [rangeMinutes, setRangeMinutes] = useState(180) const metricsQuery = useQuery({ queryKey: ['cloudwatch-metrics', accountId], queryFn: ({signal}) => listMetrics('aws', {}, signal), enabled: runtimeReachable, }) const seriesQuery = useQuery({ queryKey: ['cloudwatch-series', accountId, selected && metricKey(selected), statistic, rangeMinutes], queryFn: ({signal}) => getMetricSeries('aws', {...selected!, statistic, rangeMinutes}, signal), enabled: runtimeReachable && selected !== null, }) const metrics = useMemo(() => metricsQuery.data ?? [], [metricsQuery.data]) const namespaces = useMemo(() => [...new Set(metrics.map((metric) => metric.namespace))].sort(), [metrics]) const term = search.trim().toLowerCase() const shown = metrics.filter((metric) => (!namespace || metric.namespace === namespace) && (!term || [metric.metricName, ...metric.dimensions.map((dimension) => `${dimension.name}=${dimension.value}`)].some((value) => value.toLowerCase().includes(term)))) const error = metricsQuery.error ?? seriesQuery.error return

Metrics

{metrics.length} {metrics.length === 1 ? 'metric' : 'metrics'} in {namespaces.length} {namespaces.length === 1 ? 'namespace' : 'namespaces'}. Pick one to chart it.
{selected &&

{selected.metricName} · {selected.namespace}{selected.dimensions.map((dimension) => ` · ${dimension.name}=${dimension.value}`).join('')}

{RANGES.map((range) => ( ))}
{seriesQuery.isLoading &&

Loading metric data

} {seriesQuery.data && }
} {error instanceof Error &&

{error.message}

} {metricsQuery.isSuccess && metrics.length === 0 &&

No metrics yet

Metrics appear when services or your apps publish them, for example with PutMetricData.

} {shown.length > 0 &&
{shown.map((metric) => { const isSelected = selected !== null && metricKey(selected) === metricKey(metric) return })}
MetricNamespaceDimensions
{metric.metricName} {metric.namespace} {metric.dimensions.length ? metric.dimensions.map((dimension) => `${dimension.name}=${dimension.value}`).join(', ') : '—'}
} {metricsQuery.isSuccess && metrics.length > 0 && shown.length === 0 &&

No metrics match.

}
} function metricKey(metric: MetricSummary): string { return [metric.namespace, metric.metricName, ...metric.dimensions.map((dimension) => `${dimension.name}=${dimension.value}`)].join('|') }