@growth-operations/ui-shared 1.3.0 → 1.4.0

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.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Shared HubSpot UI-extension modules (Home, Billing) for Growth Operations apps.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -6,6 +6,7 @@ import {
6
6
  Text,
7
7
  Tile,
8
8
  LoadingButton,
9
+ Button,
9
10
  Alert,
10
11
  } from "@hubspot/ui-extensions";
11
12
  import { useStrictModeEffect } from "../lib/useStrictModeEffect";
@@ -339,13 +340,38 @@ function CreditsBilling({ context, state, appKey }) {
339
340
  );
340
341
  }
341
342
 
343
+ // Legacy (Anvil) installs are billed through their existing subscription, NOT
344
+ // the credit model. Render a calm informational panel — never the credit meter
345
+ // or plan picker, which would falsely tell a paying Anvil customer they're out
346
+ // of credits and push them to buy a credit plan.
347
+ function LegacyBilling({ state }) {
348
+ const manageUrl = state?.legacy_billing_url;
349
+ return (
350
+ <Flex direction="column" gap="medium">
351
+ <Alert title="Managed through your existing subscription" variant="info">
352
+ <Text>
353
+ Your plan and payment method are billed through your existing
354
+ subscription. There's nothing to set up here.
355
+ </Text>
356
+ </Alert>
357
+ {manageUrl && (
358
+ <Button href={{ url: manageUrl, external: true }} variant="secondary">
359
+ Manage subscription
360
+ </Button>
361
+ )}
362
+ </Flex>
363
+ );
364
+ }
365
+
342
366
  export function BillingTab({ context, state, appKey }) {
343
367
  const mode = state?.entitlement?.mode;
344
368
 
345
369
  return (
346
370
  <Flex direction="column" gap="medium">
347
371
  <Heading>Billing</Heading>
348
- {mode === "credits" ? (
372
+ {mode === "legacy" ? (
373
+ <LegacyBilling state={state} />
374
+ ) : mode === "credits" ? (
349
375
  <CreditsBilling context={context} state={state} appKey={appKey} />
350
376
  ) : (
351
377
  <TrialSubscriptionBilling context={context} state={state} appKey={appKey} />
@@ -87,8 +87,12 @@ export function UsageTab({
87
87
 
88
88
  const series = data?.series ?? [];
89
89
  const usedInWindow = data?.used_in_window ?? 0;
90
- const remaining = data?.remaining ?? 0;
91
- const granted = data?.granted ?? 0;
90
+ // Legacy (Anvil) installs have no credit balance — the backend returns null
91
+ // granted/remaining. Show only the window usage stat for them; credit installs
92
+ // get the full granted/remaining breakdown.
93
+ const remaining = data?.remaining;
94
+ const granted = data?.granted;
95
+ const hasBalance = remaining != null && granted != null;
92
96
  const hasActivity = usedInWindow > 0;
93
97
 
94
98
  return (
@@ -106,9 +110,9 @@ export function UsageTab({
106
110
 
107
111
  <Tile>
108
112
  <Statistics>
109
- <StatisticsItem label="Credits used (window)" number={usedInWindow} />
110
- <StatisticsItem label="Remaining" number={remaining} />
111
- <StatisticsItem label="Granted" number={granted} />
113
+ <StatisticsItem label="Used (window)" number={usedInWindow} />
114
+ {hasBalance && <StatisticsItem label="Remaining" number={remaining} />}
115
+ {hasBalance && <StatisticsItem label="Granted" number={granted} />}
112
116
  </Statistics>
113
117
  </Tile>
114
118