diff --git a/src/Web/autopilot-monitor-web/app/settings/reporting/sections/SectionMcpUsage.tsx b/src/Web/autopilot-monitor-web/app/settings/reporting/sections/SectionMcpUsage.tsx index aecd3a3d..15a183ed 100644 --- a/src/Web/autopilot-monitor-web/app/settings/reporting/sections/SectionMcpUsage.tsx +++ b/src/Web/autopilot-monitor-web/app/settings/reporting/sections/SectionMcpUsage.tsx @@ -25,22 +25,34 @@ interface DailyAggregate { } // GetMyMcpUsageResponse.quota — the caller's own windows plus the organization-wide windows every -// member shares. 0 = unlimited; -1 used = counters temporarily unavailable. +// member shares. 0 = unlimited; -1 used = counters temporarily unavailable. The tenant fields are +// optional on the client: a backend that predates the organization-wide quota omits them, and the +// page must keep working through that deploy window (web ships before or after the backend). interface QuotaState { dailyLimit: number; monthlyLimit: number; dailyUsed: number; monthlyUsed: number; - tenantPlan: string; - tenantDailyLimit: number; - tenantMonthlyLimit: number; - tenantDailyUsed: number; - tenantMonthlyUsed: number; + tenantPlan?: string; + tenantDailyLimit?: number; + tenantMonthlyLimit?: number; + tenantDailyUsed?: number; + tenantMonthlyUsed?: number; } -function QuotaBar({ label, used, limit }: { label: string; used: number; limit: number }) { - const unavailable = used < 0; - const unlimited = limit <= 0; +function hasTenantQuota(q: QuotaState): q is QuotaState & Required> { + return ( + typeof q.tenantDailyLimit === "number" && + typeof q.tenantMonthlyLimit === "number" && + typeof q.tenantDailyUsed === "number" && + typeof q.tenantMonthlyUsed === "number" + ); +} + +function QuotaBar({ label, used, limit }: { label: string; used: number | undefined; limit: number | undefined }) { + // Missing numbers (older backend, partial payload) render as unavailable instead of throwing. + const unavailable = typeof used !== "number" || used < 0; + const unlimited = typeof limit !== "number" || limit <= 0; const pct = unavailable || unlimited ? 0 : Math.min(100, Math.round((used / limit) * 100)); const tone = pct >= 100 ? "bg-red-500" : pct >= 80 ? "bg-amber-500" : "bg-indigo-500"; return ( @@ -50,7 +62,7 @@ function QuotaBar({ label, used, limit }: { label: string; used: number; limit: {unavailable ? "n/a" : used.toLocaleString()} {" / "} - {unlimited ? "unlimited" : limit.toLocaleString()} + {typeof limit !== "number" ? "n/a" : unlimited ? "unlimited" : limit.toLocaleString()}
@@ -200,18 +212,20 @@ export function SectionMcpUsage() {
-
-
-

Organization quota

- tenant plan {quota.tenantPlan} + {hasTenantQuota(quota) && ( +
+
+

Organization quota

+ {quota.tenantPlan && tenant plan {quota.tenantPlan}} +
+ + +

+ Shared by every account in your tenant. A personal plan override widens only the account's own + windows, never these. +

- - -

- Shared by every account in your tenant. A personal plan override widens only the account's own - windows, never these. -

-
+ )}
)}