diff --git a/packages/core/src/billing/usageDisplay.test.ts b/packages/core/src/billing/usageDisplay.test.ts index 7ae0c2c050..5509f845e8 100644 --- a/packages/core/src/billing/usageDisplay.test.ts +++ b/packages/core/src/billing/usageDisplay.test.ts @@ -264,11 +264,27 @@ describe("formatResetTime", () => { expected: "Resets shortly", }, ])("$name", ({ resetAt, expected }) => { - const result = formatResetTime(resetAt, NOW); + const result = formatResetTime(resetAt, { now: NOW }); if (expected instanceof RegExp) { expect(result).toMatch(expected); } else { expect(result).toBe(expected); } }); + + it("swaps the leading phrase when a custom label is given", () => { + const opts = { now: NOW, label: "Billing period ends" }; + expect(formatResetTime(isoAt(30 * 60 * 1000), opts)).toBe( + "Billing period ends in 30m", + ); + expect(formatResetTime(isoAt(4 * 3600 * 1000), opts)).toBe( + "Billing period ends in 4h", + ); + expect(formatResetTime(isoAt(-60_000), opts)).toBe( + "Billing period ends shortly", + ); + expect(formatResetTime(isoAt(30 * 86400 * 1000), opts)).toMatch( + /^Billing period ends [A-Za-z]+ \d+ at /, + ); + }); }); diff --git a/packages/core/src/billing/usageDisplay.ts b/packages/core/src/billing/usageDisplay.ts index 1207cf9d91..2dd67e05ce 100644 --- a/packages/core/src/billing/usageDisplay.ts +++ b/packages/core/src/billing/usageDisplay.ts @@ -88,14 +88,14 @@ export function isUsageExceeded(usage: UsageOutput): boolean { export function formatResetTime( resetAtIso: string, - now: number = Date.now(), + { now = Date.now(), label = "Resets" }: { now?: number; label?: string } = {}, ): string { const parsed = Date.parse(resetAtIso); const ms = Number.isNaN(parsed) ? 0 : Math.max(0, parsed - now); const totalMinutes = Math.ceil(ms / 60_000); - if (totalMinutes <= 0) return "Resets shortly"; - if (totalMinutes < 60) return `Resets in ${totalMinutes}m`; + if (totalMinutes <= 0) return `${label} shortly`; + if (totalMinutes < 60) return `${label} in ${totalMinutes}m`; const totalHours = ms / 3_600_000; if (totalHours < 24) { @@ -106,8 +106,8 @@ export function formatResetTime( minutes = 0; } return minutes === 0 - ? `Resets in ${hours}h` - : `Resets in ${hours}h ${minutes}m`; + ? `${label} in ${hours}h` + : `${label} in ${hours}h ${minutes}m`; } const target = new Date(now + ms); @@ -120,5 +120,5 @@ export function formatResetTime( minute: "2-digit", timeZoneName: "short", }); - return `Resets ${date} at ${time}`; + return `${label} ${date} at ${time}`; } diff --git a/packages/ui/src/features/billing/UsageButton.tsx b/packages/ui/src/features/billing/UsageButton.tsx index f6fce7457e..1a4432b4a1 100644 --- a/packages/ui/src/features/billing/UsageButton.tsx +++ b/packages/ui/src/features/billing/UsageButton.tsx @@ -68,9 +68,10 @@ export function UsageButton() { meter.kind === "dollars" ? `${formatUsdAmount(meter.usedUsd)} of ${formatUsdAmount(meter.limitUsd)} used` : `${percent}% used`; - const resetLabel = formatResetTime( - meter.kind === "dollars" ? meter.resetAt : meter.bucket.reset_at, - ); + const resetLabel = + meter.kind === "dollars" + ? formatResetTime(meter.resetAt, { label: "Billing period ends" }) + : formatResetTime(meter.bucket.reset_at); const breakdownLabel = meter.kind === "dollars" && meter.breakdown ? formatUsageBreakdown(meter.breakdown) diff --git a/packages/ui/src/features/settings/sections/PlanUsageSettings.tsx b/packages/ui/src/features/settings/sections/PlanUsageSettings.tsx index e794e5afd1..30811960f5 100644 --- a/packages/ui/src/features/settings/sections/PlanUsageSettings.tsx +++ b/packages/ui/src/features/settings/sections/PlanUsageSettings.tsx @@ -30,12 +30,6 @@ import { getBillingUrl } from "@posthog/ui/utils/urls"; import { Badge, Button, Callout, Flex, Spinner, Text } from "@radix-ui/themes"; import { useEffect } from "react"; -/** - * Plan & usage under usage-based billing: no seats or plans to manage in-app — - * payment methods, spend limits, and org-level usage live on the PostHog - * billing page. The free-tier meters show the per-user allowance for - * confirmed free-tier orgs; subscribed orgs have no per-user caps to meter. - */ export function PlanUsageSettings() { const billingEnabled = useFeatureFlag(BILLING_FLAG); const spendAnalysisEnabled = useSpendAnalysisEnabled(); @@ -163,7 +157,9 @@ export function PlanUsageSettings() { - Usage + + Organization usage + {usageLoading ? ( - Spend analysis + Personal spend analysis