${canRunPurchase ? `` : ''}
${canPauseOrResumePurchase && isPending ? `` : ''}
@@ -627,7 +628,7 @@ interface BackendPlan {
// Score bands for the per-plan health badge (issue #340 follow-up). Green
// >= 80 (healthy), amber 50-79 (needs attention), red < 50 (action needed).
-// Reuses the existing status-badge palette (badge-success/badge-warning/
+// Reuses the shared .badge tones (badge-success/badge-warning/
// badge-danger) already used elsewhere on this card rather than introducing
// new CSS.
const HEALTH_SCORE_GOOD_THRESHOLD = 80;
@@ -680,14 +681,14 @@ function healthBadgeHtml(plan: BackendPlan): string {
// execution history, while a non-finite value means the number itself
// arrived unusable. Naming only the first would send an operator
// chasing a database problem that may not exist.
- return `Health: unknown`;
+ return `Health: unknown`;
}
const factors = plan.health_factors || [];
const factorLines = factors.length > 0
? factors.map(f => `-${f.penalty}: ${f.note}`)
: ['No issues detected'];
const tooltip = [`Plan health: ${plan.health_score}/100`, ...factorLines].join('\n');
- return `Health: ${plan.health_score}`;
+ return `Health: ${plan.health_score}`;
}
// Pretty label for a service slug used inside the plan card.
@@ -816,7 +817,7 @@ function renderPlanCard(plan: BackendPlan, canManagePlan: boolean, canDeletePlan
// visible implies the plan will still run on that date, which it won't.
const showNextDate = Boolean(plan.next_execution_date) && plan.enabled;
const overdueBadge = overdue && plan.enabled
- ? 'Overdue'
+ ? 'Overdue'
: '';
// Read-only mode: unassigned plans cannot be purchased against until an
// operator re-assigns them to at least one account.
@@ -827,7 +828,7 @@ function renderPlanCard(plan: BackendPlan, canManagePlan: boolean, canDeletePlan
${escapeHtml(plan.name)}
- ${status.label}
+ ${status.label}
${healthBadgeHtml(plan)}
${overdueBadge}
${canManagePlan && !isUnassigned ? `
diff --git a/frontend/src/riexchange.ts b/frontend/src/riexchange.ts
index c419f5d1..2d973389 100644
--- a/frontend/src/riexchange.ts
+++ b/frontend/src/riexchange.ts
@@ -31,6 +31,7 @@ import type {
} from './api';
import { openModal, closeModal } from './modal';
import { showSkeletonRows, showSkeletonText, teardownSkeleton } from './lib/skeleton';
+import { statusBadgeHtml } from './lib/status-badge';
import { canAccess } from './permissions';
import { createIcon, iconSvg } from './icons';
import { applyReadOnlySettings, isPermissionDeniedError } from './settings';
@@ -1994,24 +1995,6 @@ async function loadExchangeHistory(generation: number = scopeGeneration): Promis
}
}
-function getStatusBadgeClass(status: string): string {
- switch (status) {
- case 'completed': return 'status-badge completed';
- case 'pending': return 'status-badge pending';
- case 'processing': return 'status-badge running';
- case 'failed': return 'status-badge failed';
- // Migration 000089 (expand-contract rename, ri_exchange_history.status):
- // backend may return either spelling during the rolling deploy window.
- // Match both so a row written by EITHER old or new code keeps the muted
- // "disabled" visual treatment instead of falling through to the default
- // class. The CONTRACT migration (#1278) normalizes the data; the British
- // branch can be removed then.
- case 'canceled':
- case 'cancelled': return 'status-badge disabled';
- default: return 'status-badge';
- }
-}
-
function renderExchangeHistory(container: HTMLElement, records: RIExchangeHistoryRecord[]): void {
if (!records || records.length === 0) {
container.textContent = '';
@@ -2042,7 +2025,7 @@ function renderExchangeHistory(container: HTMLElement, records: RIExchangeHistor
// payment_due is USD (the execute cap is max_payment_due_usd); an
// empty amount renders as a dash, not a lone "$" (issue #284).
+ '