From f1a6b4aa70da03567663fbd0f3149aed99e53e3d Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Tue, 21 Apr 2026 11:55:23 -0600 Subject: [PATCH] feat(vps): show provision + deprecation reason on each fleet row Pairs with lantern-cloud #2623 which persists provisionReason / deprecationReason on the vps_routes table. Renders them as a small badge next to the status/deprecated badge on each fleet row so operators can answer "why is this VPS deprecated?" / "why is this route spinning up?" without leaving the tab. - Deprecation reason renders in red-muted next to the red "deprecated" badge (e.g. "blocked (grace)", "manual", "track deleted"). - Provision reason renders in neutral grey next to the status badge, and only for non-running routes since a running route's provision history is less interesting than whatever's actively happening now. - REASON_LABELS maps known codes to friendlier text; unknown codes fall through with underscores replaced by spaces so future API additions degrade gracefully. --- src/api/client.ts | 2 ++ src/components/VPSOverview.tsx | 54 +++++++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/api/client.ts b/src/api/client.ts index 01fc954..2123666 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -217,6 +217,8 @@ export interface DashboardVPSRoute { vpsInstanceId?: string; assignmentCount: number; peakAssignmentCount: number; + provisionReason?: string; + deprecationReason?: string; trackName: string; protocolName: string; locationName?: string; diff --git a/src/components/VPSOverview.tsx b/src/components/VPSOverview.tsx index 180e8dc..2df4ba6 100644 --- a/src/components/VPSOverview.tsx +++ b/src/components/VPSOverview.tsx @@ -34,6 +34,24 @@ function formatUptime(createdISO: string): string { return `${minutes}m`; } +// Short human-friendly labels for the reason codes the API stamps on +// vps_routes.provision_reason / deprecation_reason. Unknown codes fall +// through with underscores replaced by spaces so they still read cleanly +// on screen (e.g. a future reason code ships before the UI is updated). +const REASON_LABELS: Record = { + pool_deficit: "pool deficit", + capacity_scale_up: "capacity scale-up", + admin_create: "admin create", + blocked_grace: "blocked (grace)", + manual: "manual", + track_deleted: "track deleted", + force_release: "force released", +}; + +function formatReason(code: string): string { + return REASON_LABELS[code] || code.replace(/_/g, " "); +} + interface RegionGroup { regionName: string; city?: string; @@ -415,16 +433,36 @@ function VPSOverview({ routes, summary, isLoading, error }: VPSOverviewProps) { / {route.peakAssignmentCount} - {/* Status / Deprecated badge */} - + {/* Status / Deprecated badge + reason chip */} + {isDeprecated ? ( - - deprecated - + <> + + deprecated + + {route.deprecationReason && ( + + {formatReason(route.deprecationReason)} + + )} + ) : ( - - {route.status} - + <> + + {route.status} + + {route.provisionReason && route.status !== "running" && ( + + {formatReason(route.provisionReason)} + + )} + )}