@growth-operations/ui-shared 1.13.0 → 1.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growth-operations/ui-shared",
3
- "version": "1.13.0",
3
+ "version": "1.13.2",
4
4
  "description": "Shared HubSpot UI-extension modules (Home, Billing) for Growth Operations apps.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -358,8 +358,13 @@ function CreditsBilling({ context, state, appKey, openIframe = null }) {
358
358
  // current tier and disable everything ranked below it ("Talk to sales to
359
359
  // switch") — the backend independently re-validates the same upgrade
360
360
  // direction (start_upgrade_redirect's tier_order check), so this is safe
361
- // even if currentOrder were ever wrong. Downgrade/cancel/payment method/
362
- // invoices still go through the Stripe Customer Portal link below.
361
+ // even if currentOrder were ever wrong. Downgrade is intentionally NOT
362
+ // self-serve (policy, not a Stripe portal gap): PlanCard's own
363
+ // "Talk to sales to switch" is the one and only downgrade path. Cancel /
364
+ // payment method / invoices still go through the Stripe Customer Portal
365
+ // link below — the footnote must not imply Manage subscription can also
366
+ // downgrade (some apps' Stripe portal config has subscription_update
367
+ // disabled anyway, so it silently couldn't even if we wanted it to).
363
368
  if (onPaidPlan) {
364
369
  const plans = state?.plans ?? [];
365
370
  const currentOrder = plans.find((p) => p.current)?.tier_order;
@@ -388,7 +393,7 @@ function CreditsBilling({ context, state, appKey, openIframe = null }) {
388
393
  endpoint="upgrade/start"
389
394
  ctaLabel="Upgrade to"
390
395
  heading="Your plan"
391
- footnote="Upgrade any time — the new tier applies immediately with prorated billing. To move to a lower tier, cancel, or update your payment method, use Manage subscription above."
396
+ footnote="Upgrade any time — the new tier applies immediately with prorated billing. To move to a lower tier, contact us. Use Manage subscription above to cancel or update your payment method."
392
397
  openIframe={openIframe}
393
398
  />
394
399
  ) : (
@@ -24,14 +24,31 @@ export function CreditMeter({ entitlement, creditMeter }) {
24
24
  const used = meter?.used ?? ent?.used ?? 0;
25
25
  const remaining = meter?.remaining ?? ent?.remaining ?? Math.max(granted - used, 0);
26
26
 
27
+ // A paid plan is NEVER blocked — exceeding the included bucket is overage,
28
+ // not depletion (action_allowed_by_portal/consume_credits never gate a
29
+ // paid plan; see common.entitlements.credits.to_entitlement). So `depleted`
30
+ // must not fall back to raw `remaining <= 0` on a paid plan the way it does
31
+ // on the free tier — that raw check is exactly what showed a paying,
32
+ // already-subscribed customer a blocking "Depleted"/"out of credits" panel
33
+ // (confirmed live 2026-09-02). `over_included` is the distinct paid-plan
34
+ // signal for "you've used your included credits, extra usage is overage".
35
+ const onPaidPlan = !!ent?.plan;
27
36
  const lowThreshold = ent?.low_threshold ?? 0;
28
- const depleted = ent?.depleted === true || remaining <= 0;
29
- const low = !depleted && remaining <= lowThreshold;
37
+ const depleted = !onPaidPlan && (ent?.depleted === true || remaining <= 0);
38
+ const overIncluded =
39
+ onPaidPlan && (ent?.over_included === true || used > granted);
40
+ const low = !depleted && !overIncluded && remaining <= lowThreshold;
30
41
 
31
- // healthy green / low yellow / depleted red.
32
- const variant = depleted ? "error" : low ? "warning" : "success";
33
- const tagVariant = depleted ? "danger" : low ? "warning" : "success";
34
- const tagLabel = depleted ? "Depleted" : low ? "Running low" : "Healthy";
42
+ // healthy green / low or overage yellow / depleted red.
43
+ const variant = depleted ? "error" : overIncluded || low ? "warning" : "success";
44
+ const tagVariant = depleted ? "danger" : overIncluded || low ? "warning" : "success";
45
+ const tagLabel = depleted
46
+ ? "Depleted"
47
+ : overIncluded
48
+ ? "Over included"
49
+ : low
50
+ ? "Running low"
51
+ : "Healthy";
35
52
 
36
53
  const grantDaysLeft = ent?.grant_expires_at ? daysUntil(ent.grant_expires_at) : null;
37
54
 
@@ -65,15 +82,23 @@ export function CreditMeter({ entitlement, creditMeter }) {
65
82
  </Text>
66
83
  )}
67
84
 
68
- {(depleted || low) && (
85
+ {(depleted || overIncluded || low) && (
69
86
  <Alert
70
- title={depleted ? "You're out of credits" : "Running low on credits"}
87
+ title={
88
+ depleted
89
+ ? "You're out of credits"
90
+ : overIncluded
91
+ ? "You've used your included credits"
92
+ : "Running low on credits"
93
+ }
71
94
  variant={variant}
72
95
  >
73
96
  <Text>
74
97
  {depleted
75
98
  ? "Pick a plan or buy more credits to keep going."
76
- : "You're getting close to your limit — pick a plan to top up."}
99
+ : overIncluded
100
+ ? "Extra usage this period is billed as overage at your plan's per-credit rate."
101
+ : "You're getting close to your limit — pick a plan to top up."}
77
102
  </Text>
78
103
  </Alert>
79
104
  )}