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'