@growth-operations/ui-shared 1.0.3 → 1.0.4

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.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Shared HubSpot UI-extension modules (Home, Billing) for Growth Operations apps.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -5,8 +5,7 @@ import {
5
5
  Heading,
6
6
  Text,
7
7
  Tile,
8
- Link,
9
- LoadingSpinner,
8
+ LoadingButton,
10
9
  Alert,
11
10
  } from "@hubspot/ui-extensions";
12
11
  import { useStrictModeEffect } from "../lib/useStrictModeEffect";
@@ -41,6 +40,10 @@ function StatusPanel({ entitlement }) {
41
40
  cancel_at_period_end,
42
41
  } = entitlement;
43
42
 
43
+ // HubSpot's Alert renders `title` beside the body in a narrow column, which
44
+ // squeezes multi-line status detail into awkward wraps. Put the headline as a
45
+ // bold first line INSIDE the body (full width) and leave the Alert title unset
46
+ // so the detail flows across the panel.
44
47
  if (status === "trialing") {
45
48
  const trialEnd = trial_ends_at || current_period_end || sandbox_trial_expires_at;
46
49
  const left = daysUntil(trialEnd);
@@ -51,21 +54,27 @@ function StatusPanel({ entitlement }) {
51
54
  ? "Your free trial ends today"
52
55
  : `${left} day${left === 1 ? "" : "s"} left in your free trial`;
53
56
  return (
54
- <Alert title={leftLabel} variant="warning">
55
- <Text>
56
- Trial ends {fmtDate(trialEnd)}. Add a plan in Stripe before then to
57
- keep syncing.
58
- </Text>
57
+ <Alert variant="warning">
58
+ <Flex direction="column" gap="extra-small">
59
+ <Text format={{ fontWeight: "bold" }}>{leftLabel}</Text>
60
+ <Text>
61
+ Trial ends {fmtDate(trialEnd)}. Add a plan in Stripe before then to
62
+ keep syncing.
63
+ </Text>
64
+ </Flex>
59
65
  </Alert>
60
66
  );
61
67
  }
62
68
 
63
69
  if (status === "active") {
64
70
  return (
65
- <Alert title="You're all set" variant="success">
71
+ <Alert variant="success">
66
72
  <Flex direction="column" gap="extra-small">
67
- <Text>Your subscription is active.</Text>
68
- <Text>Next renewal: {fmtDate(current_period_end)}</Text>
73
+ <Text format={{ fontWeight: "bold" }}>You're all set</Text>
74
+ <Text>
75
+ Your subscription is active. Next renewal:{" "}
76
+ {fmtDate(current_period_end)}.
77
+ </Text>
69
78
  {cancel_at_period_end && (
70
79
  <Text format={{ fontStyle: "italic" }}>
71
80
  Cancels at the end of the current period.
@@ -78,19 +87,27 @@ function StatusPanel({ entitlement }) {
78
87
 
79
88
  if (status === "past_due") {
80
89
  return (
81
- <Alert title="Your subscription is past due" variant="error">
82
- <Text>
83
- We couldn't process your latest payment. Update your payment method in
84
- Stripe to avoid losing access.
85
- </Text>
90
+ <Alert variant="error">
91
+ <Flex direction="column" gap="extra-small">
92
+ <Text format={{ fontWeight: "bold" }}>
93
+ Your subscription is past due
94
+ </Text>
95
+ <Text>
96
+ We couldn't process your latest payment. Update your payment method
97
+ in Stripe to avoid losing access.
98
+ </Text>
99
+ </Flex>
86
100
  </Alert>
87
101
  );
88
102
  }
89
103
 
90
104
  if (status === "pending_purchase") {
91
105
  return (
92
- <Alert title="Your free trial has ended" variant="warning">
93
- <Text>Choose a plan in Stripe to resume syncing your data.</Text>
106
+ <Alert variant="warning">
107
+ <Flex direction="column" gap="extra-small">
108
+ <Text format={{ fontWeight: "bold" }}>Your free trial has ended</Text>
109
+ <Text>Choose a plan in Stripe to resume syncing your data.</Text>
110
+ </Flex>
94
111
  </Alert>
95
112
  );
96
113
  }
@@ -173,19 +190,23 @@ function TrialSubscriptionBilling({ context, state, appKey }) {
173
190
 
174
191
  return (
175
192
  <Flex direction="column" gap="medium">
176
- {/* PRIMARY CTA at the very top, large and obvious (per billing feedback). */}
177
- {portalUrl ? (
178
- <Link href={{ url: portalUrl, external: true }} variant="primary">
179
- Manage billing in Stripe
180
- </Link>
181
- ) : loading ? (
182
- <LoadingSpinner showLabel label="Preparing billing…" />
193
+ {/* PRIMARY CTA at the very top, large and obvious (per billing feedback).
194
+ A Button with href navigates to the portal (Button supports href +
195
+ external) so it reads as a real button, not a text link; LoadingButton
196
+ shows the prepared/loading state inline. */}
197
+ {error ? (
198
+ <Alert title="Couldn't open billing" variant="danger">
199
+ <Text>{error}</Text>
200
+ </Alert>
183
201
  ) : (
184
- error && (
185
- <Alert title="Couldn't open billing" variant="danger">
186
- <Text>{error}</Text>
187
- </Alert>
188
- )
202
+ <LoadingButton
203
+ href={portalUrl ? { url: portalUrl, external: true } : undefined}
204
+ loading={loading}
205
+ disabled={!portalUrl}
206
+ variant="primary"
207
+ >
208
+ {portalUrl ? "Manage billing in Stripe" : "Preparing billing…"}
209
+ </LoadingButton>
189
210
  )}
190
211
 
191
212
  <StatusPanel entitlement={state?.entitlement} />
@@ -212,19 +233,21 @@ function CreditsBilling({ context, state, appKey }) {
212
233
  creditMeter={state?.credit_meter}
213
234
  />
214
235
 
215
- {/* PRIMARY CTA at top: pick a plan / buy credits. */}
216
- {portalUrl ? (
217
- <Link href={{ url: portalUrl, external: true }} variant="primary">
218
- Pick a plan or buy credits
219
- </Link>
220
- ) : loading ? (
221
- <LoadingSpinner showLabel label="Preparing checkout…" />
236
+ {/* PRIMARY CTA at top: pick a plan / buy credits. Button-with-href so it
237
+ reads as a real button; LoadingButton shows the prepared/loading state. */}
238
+ {error ? (
239
+ <Alert title="Couldn't open checkout" variant="danger">
240
+ <Text>{error}</Text>
241
+ </Alert>
222
242
  ) : (
223
- error && (
224
- <Alert title="Couldn't open checkout" variant="danger">
225
- <Text>{error}</Text>
226
- </Alert>
227
- )
243
+ <LoadingButton
244
+ href={portalUrl ? { url: portalUrl, external: true } : undefined}
245
+ loading={loading}
246
+ disabled={!portalUrl}
247
+ variant="primary"
248
+ >
249
+ {portalUrl ? "Pick a plan or buy credits" : "Preparing checkout…"}
250
+ </LoadingButton>
228
251
  )}
229
252
 
230
253
  <Text format={{ fontStyle: "italic" }}>