import { useState } from 'react' import { usePoller } from '../hooks/usePoller.ts' import { useI18n } from '../hooks/useI18n.ts' import { monitorApi } from '../api/monitorApi.ts' import type { MetricsSummary, CacheStats, ProcessingDelay, CacheDetails } from '../api/types.ts' import MetricCard from '../components/charts/MetricCard.tsx' import { formatNumber } from '../utils/formatters.ts' const POLL_INTERVAL = 10_000 export default function ApiMetrics() { const { t } = useI18n() const [metrics, setMetrics] = useState(null) const [cache, setCache] = useState(null) const [cacheDetails, setCacheDetails] = useState(null) const [delay, setDelay] = useState(null) usePoller(async () => { const [m, c, cd, d] = await Promise.allSettled([ monitorApi.getMetricsSummary(), monitorApi.getCacheStats(), monitorApi.getCacheDetails(), monitorApi.getDelay(), ]) if (m.status === 'fulfilled') setMetrics(m.value) if (c.status === 'fulfilled') setCache(c.value) if (cd.status === 'fulfilled') setCacheDetails(cd.value) if (d.status === 'fulfilled') setDelay(d.value) }, POLL_INTERVAL) const memUsed = metrics?.memory.used ?? 0 const memMax = metrics?.memory.max ?? 1 const memPct = Math.round((memUsed / memMax) * 100) return (

{t('metrics.title')}

{/* System Metrics */}
85 ? 'down' : memPct > 70 ? 'neutral' : 'up'} />
{/* Cache Detail Grid */}
{t('metrics.cacheDetail')}
{cacheDetails ? (
{cacheDetails.l1_fiveMin && ( )} {cacheDetails.l2_hourly && ( )} {cacheDetails.l3_daily && ( )} {cacheDetails.aisTarget && ( )}
{t('metrics.cacheLayer')} {t('metrics.size')} {t('metrics.maxSize')} {t('metrics.utilization')} {t('metrics.hitRate')}
L1 (5min) {formatNumber(cacheDetails.l1_fiveMin.size)} {formatNumber(cacheDetails.l1_fiveMin.maxSize)} {((cacheDetails.l1_fiveMin.size / Math.max(cacheDetails.l1_fiveMin.maxSize, 1)) * 100).toFixed(1)}% {cacheDetails.l1_fiveMin.hitRate?.toFixed(1) ?? '-'}%
L2 (Hourly) {formatNumber(cacheDetails.l2_hourly.size)} {formatNumber(cacheDetails.l2_hourly.maxSize)} {((cacheDetails.l2_hourly.size / Math.max(cacheDetails.l2_hourly.maxSize, 1)) * 100).toFixed(1)}% {cacheDetails.l2_hourly.hitRate?.toFixed(1) ?? '-'}%
L3 (Daily) {cacheDetails.l3_daily.cachedDays ?? 0} days {cacheDetails.l3_daily.retentionDays ?? '-'} days {cacheDetails.l3_daily.totalMemoryMb?.toFixed(0) ?? 0} MB {cacheDetails.l3_daily.totalVessels ?? 0} vessels
AIS Target {formatNumber(cacheDetails.aisTarget.estimatedSize)} {formatNumber(cacheDetails.aisTarget.maxSize)} {((cacheDetails.aisTarget.estimatedSize / Math.max(cacheDetails.aisTarget.maxSize, 1)) * 100).toFixed(1)}% {cacheDetails.aisTarget.hitRate?.toFixed(1) ?? '-'}%
) : (
{t('common.loading')}
)}
{/* Processing & Cache Summary */}
{t('metrics.processingDelay')}
{delay ? (
{t('metrics.delayMinutes')}
{delay.delayMinutes ?? 0} min
{t('metrics.aisCount')}
{formatNumber(delay.recentAisCount)}
{t('metrics.processedVessels')}
{formatNumber(delay.processedVessels)}
{t('metrics.status')}
{delay.status}
) : (
{t('common.loading')}
)}
{t('metrics.cacheHitSummary')}
{cache ? (
{cache.hitRate}
{t('metrics.hitRate')}
{formatNumber(cache.hitCount)}
{t('metrics.hits')}
{formatNumber(cache.missCount)}
{t('metrics.misses')}
) : (
{t('common.loading')}
)}
{/* Placeholder for future DB-based metrics */}

{t('metrics.dbMetricsPlaceholder')}

{t('metrics.dbMetricsDesc')}

) }