diff --git a/src/features/dashboard/layouts/details-row.tsx b/src/features/dashboard/layouts/details-row.tsx index c5425f3a7..42f86a4c9 100644 --- a/src/features/dashboard/layouts/details-row.tsx +++ b/src/features/dashboard/layouts/details-row.tsx @@ -16,10 +16,15 @@ export function DetailsItem({ label, children, ...props }: DetailItemProps) { interface DetailsRowProps { children: ReactNode + className?: string } -export function DetailsRow({ children }: DetailsRowProps) { +export function DetailsRow({ children, className }: DetailsRowProps) { return ( -
{children}
+
+ {children} +
) } diff --git a/src/features/dashboard/sandbox/header/header.tsx b/src/features/dashboard/sandbox/header/header.tsx index ae0362a5e..cc22bfe5f 100644 --- a/src/features/dashboard/sandbox/header/header.tsx +++ b/src/features/dashboard/sandbox/header/header.tsx @@ -1,5 +1,6 @@ 'use client' +import type { ReactNode } from 'react' import { Skeleton } from '@/ui/primitives/skeleton' import { DetailsItem, DetailsRow } from '../../layouts/details-row' import { useSandboxContext } from '../context' @@ -12,6 +13,12 @@ import StartedAt from './started-at' import Status from './status' import TemplateId from './template-id' +interface HeaderDetailItem { + label: string + content: ReactNode + skeletonWidth: string +} + export default function SandboxDetailsHeader() { const { isRunning, sandboxInfo, isSandboxInfoLoading } = useSandboxContext() @@ -39,36 +46,30 @@ export default function SandboxDetailsHeader() { const renderContent = (content: React.ReactNode, skeletonWidth: string) => isInitialLoading ? : content + const items: HeaderDetailItem[] = [ + { label: 'status', content: statusContent, skeletonWidth: 'w-20' }, + { label: 'template', content: templateContent, skeletonWidth: 'w-28' }, + { label: 'metadata', content: metadataContent, skeletonWidth: 'w-20' }, + { label: 'created at', content: createdAtContent, skeletonWidth: 'w-32' }, + { label: timeoutLabel, content: timeoutContent, skeletonWidth: 'w-22' }, + { + label: runningLabel, + content: runningDurationContent, + skeletonWidth: 'w-22', + }, + { label: 'CPU', content: cpuSpecContent, skeletonWidth: 'w-20' }, + { label: 'Memory', content: memorySpecContent, skeletonWidth: 'w-20' }, + { label: 'Disk', content: diskSpecContent, skeletonWidth: 'w-20' }, + ] + return ( -
- - - {renderContent(statusContent, 'w-20')} - - - {renderContent(templateContent, 'w-28')} - - - {renderContent(metadataContent, 'w-20')} - - - {renderContent(createdAtContent, 'w-32')} - - - {renderContent(timeoutContent, 'w-22')} - - - {renderContent(runningDurationContent, 'w-22')} - - - {renderContent(cpuSpecContent, 'w-20')} - - - {renderContent(memorySpecContent, 'w-20')} - - - {renderContent(diskSpecContent, 'w-20')} - +
+ + {items.map((item) => ( + + {renderContent(item.content, item.skeletonWidth)} + + ))}
) diff --git a/src/features/dashboard/sandbox/monitoring/components/monitoring-sandbox-metrics-chart.tsx b/src/features/dashboard/sandbox/monitoring/components/monitoring-sandbox-metrics-chart.tsx index 2cdd149f4..e49bd9f97 100644 --- a/src/features/dashboard/sandbox/monitoring/components/monitoring-sandbox-metrics-chart.tsx +++ b/src/features/dashboard/sandbox/monitoring/components/monitoring-sandbox-metrics-chart.tsx @@ -36,7 +36,6 @@ import { SANDBOX_MONITORING_CHART_FG_VAR, SANDBOX_MONITORING_CHART_LINE_WIDTH, SANDBOX_MONITORING_CHART_STROKE_VAR, - SANDBOX_MONITORING_LIVE_DATA_THRESHOLD_MS, } from '../utils/constants' import { ChartOverlayLayer } from './chart-overlays' import { useChartOverlays } from './use-chart-overlays' @@ -60,7 +59,6 @@ const CHART_CONNECTOR_LINE_OPACITY = 0.8 const CHART_OUT_OF_BRUSH_ALPHA = 0.25 const CHART_AXIS_LABEL_FONT_SIZE = 12 const CHART_Y_AXIS_SCALE_FACTOR = 1.5 -const CHART_LIVE_WINDOW_STEPS = 2 const CHART_LIVE_OUTER_DOT_SIZE = 16 const CHART_LIVE_MIDDLE_DOT_SIZE = 10 const CHART_LIVE_INNER_DOT_SIZE = 6 @@ -306,10 +304,10 @@ function SandboxMetricsChart({ [isMobile] ) - const liveWindowMs = useMemo(() => { + const liveMetricThresholdMs = useMemo(() => { const duration = xAxisMax !== undefined && xAxisMin !== undefined ? xAxisMax - xAxisMin : 0 - return CHART_LIVE_WINDOW_STEPS * calculateStepForDuration(duration) + return calculateStepForDuration(duration) }, [xAxisMax, xAxisMin]) const option = useMemo(() => { @@ -370,12 +368,8 @@ function SandboxMetricsChart({ const livePoint = isPolling && latestPointTimestampMs !== null && - Date.now() - latestPointTimestampMs <= - Math.max(liveWindowMs, SANDBOX_MONITORING_LIVE_DATA_THRESHOLD_MS) - ? findLivePoint( - line.data, - Math.max(liveWindowMs, SANDBOX_MONITORING_LIVE_DATA_THRESHOLD_MS) - ) + Date.now() - latestPointTimestampMs <= liveMetricThresholdMs + ? findLivePoint(line.data, liveMetricThresholdMs) : null const regularSeriesItems = renderableSegments.map( @@ -546,7 +540,7 @@ function SandboxMetricsChart({ grid, isMobile, isPolling, - liveWindowMs, + liveMetricThresholdMs, series, showXAxisLabels, stroke, diff --git a/src/features/dashboard/sandbox/monitoring/state/use-sandbox-monitoring-controller.ts b/src/features/dashboard/sandbox/monitoring/state/use-sandbox-monitoring-controller.ts index 438ec87b5..de26e2708 100644 --- a/src/features/dashboard/sandbox/monitoring/state/use-sandbox-monitoring-controller.ts +++ b/src/features/dashboard/sandbox/monitoring/state/use-sandbox-monitoring-controller.ts @@ -5,6 +5,7 @@ import { parseAsInteger, parseAsString, useQueryStates } from 'nuqs' import { useCallback, useEffect, useMemo, useRef } from 'react' import { useDashboard } from '@/features/dashboard/context' import { useSandboxContext } from '@/features/dashboard/sandbox/context' +import { calculateStepForDuration } from '@/features/dashboard/sandboxes/monitoring/utils' import { getMsUntilNextAlignedInterval } from '@/lib/hooks/use-aligned-refetch-interval' import type { SandboxMetric } from '@/server/api/models/sandboxes.models' import { useTRPCClient } from '@/trpc/client' @@ -12,7 +13,6 @@ import { SANDBOX_LIFECYCLE_EVENT_KILLED, SANDBOX_MONITORING_DEFAULT_PRESET_ID, SANDBOX_MONITORING_DEFAULT_RANGE_MS, - SANDBOX_MONITORING_LIVE_DATA_THRESHOLD_MS, SANDBOX_MONITORING_LIVE_POLLING_MS, SANDBOX_MONITORING_OVERFETCH_MIN_MS, SANDBOX_MONITORING_OVERFETCH_RATIO, @@ -256,11 +256,15 @@ export function useSandboxMonitoringController(sandboxId: string) { return latest }, [metricsQuery.data]) + const liveMetricThresholdMs = useMemo( + () => calculateStepForDuration(fetchTimeframe.end - fetchTimeframe.start), + [fetchTimeframe.end, fetchTimeframe.start] + ) + const isLive = shouldPoll && latestMetricTimestampMs !== null && - Date.now() - latestMetricTimestampMs <= - SANDBOX_MONITORING_LIVE_DATA_THRESHOLD_MS + Date.now() - latestMetricTimestampMs <= liveMetricThresholdMs return { lifecycleBounds, diff --git a/src/features/dashboard/sandbox/monitoring/utils/constants.ts b/src/features/dashboard/sandbox/monitoring/utils/constants.ts index c97cb8295..31d461fad 100644 --- a/src/features/dashboard/sandbox/monitoring/utils/constants.ts +++ b/src/features/dashboard/sandbox/monitoring/utils/constants.ts @@ -1,7 +1,6 @@ import { millisecondsInDay, millisecondsInHour, - millisecondsInMinute, millisecondsInSecond, } from 'date-fns/constants' @@ -55,8 +54,6 @@ export const SANDBOX_MONITORING_LIVE_POLLING_MS = 5_000 export const SANDBOX_MONITORING_CUSTOM_END_FUTURE_MS = millisecondsInHour export const SANDBOX_MONITORING_OVERFETCH_RATIO = 0.02 export const SANDBOX_MONITORING_OVERFETCH_MIN_MS = 30 * millisecondsInSecond -export const SANDBOX_MONITORING_LIVE_DATA_THRESHOLD_MS = - 2 * millisecondsInMinute export const SANDBOX_MONITORING_CHART_STROKE_VAR = '--stroke' export const SANDBOX_MONITORING_CHART_FALLBACK_STROKE = '#000'