@growth-operations/ui-shared 1.6.4 → 1.6.5
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 +1 -1
- package/src/billing/BillingTab.jsx +23 -19
- package/src/billing/PlanGrid.jsx +34 -3
package/package.json
CHANGED
|
@@ -180,21 +180,22 @@ function TrialSubscriptionBilling({ context, state, appKey }) {
|
|
|
180
180
|
`&return_url=${encodeURIComponent(returnUrl)}`
|
|
181
181
|
: null;
|
|
182
182
|
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
183
|
+
// Tier picker — shown ONLY while trialing (the lifecycle: trial starts on the
|
|
184
|
+
// entry tier at install; while trialing the customer may upgrade to a higher
|
|
185
|
+
// tier in-app; once active, plan changes go through support). We show the FULL
|
|
186
|
+
// ladder for context so the customer sees where they sit:
|
|
187
|
+
// current tier -> "Current plan" (disabled marker; PlanCard keys off
|
|
188
|
+
// plan.current, stamped server-side on the account's tier).
|
|
189
|
+
// higher tiers -> the actionable upgrade (CTA targets /v1/billing/upgrade/
|
|
190
|
+
// start, swapping the trialing sub's item — no new sub).
|
|
191
|
+
// lower tiers -> shown disabled with a Talk-to-sales CTA (downgrades aren't
|
|
192
|
+
// self-serve). PlanCard derives this from currentOrder.
|
|
189
193
|
const isTrialing = ent.status === "trialing";
|
|
190
194
|
const plans = state?.plans ?? [];
|
|
191
195
|
const currentOrder = plans.find((p) => p.current)?.tier_order;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
(p) => !p.talk_to_sales && p.tier_order != null && p.tier_order > currentOrder
|
|
196
|
-
)
|
|
197
|
-
: [];
|
|
196
|
+
// Render the picker only while trialing AND once we know the current tier
|
|
197
|
+
// (currentOrder drives upgrade-vs-downgrade per card).
|
|
198
|
+
const showPicker = isTrialing && currentOrder != null && plans.length > 0;
|
|
198
199
|
|
|
199
200
|
return (
|
|
200
201
|
<Flex direction="column" gap="medium">
|
|
@@ -215,19 +216,22 @@ function TrialSubscriptionBilling({ context, state, appKey }) {
|
|
|
215
216
|
link above to update your plan, payment method, or view invoices.
|
|
216
217
|
</Text>
|
|
217
218
|
|
|
218
|
-
{/* Trial-only
|
|
219
|
-
|
|
220
|
-
tier, keeping the trial end date.
|
|
221
|
-
|
|
219
|
+
{/* Trial-only tier ladder: current (marked), higher (upgradeable), lower
|
|
220
|
+
(disabled, Talk-to-sales). Hidden once active. Upgrading swaps the
|
|
221
|
+
trialing sub onto the higher tier, keeping the trial end date. The full
|
|
222
|
+
`plans` list is passed; PlanCard decides each card's state from
|
|
223
|
+
plan.current + currentOrder. */}
|
|
224
|
+
{showPicker && (
|
|
222
225
|
<PlanGrid
|
|
223
226
|
context={context}
|
|
224
227
|
state={state}
|
|
225
228
|
appKey={appKey}
|
|
226
|
-
plans={
|
|
229
|
+
plans={plans}
|
|
230
|
+
currentOrder={currentOrder}
|
|
227
231
|
endpoint="upgrade/start"
|
|
228
232
|
ctaLabel="Upgrade to"
|
|
229
|
-
heading="
|
|
230
|
-
footnote="Upgrade any time during your trial — your trial end date stays the same, and the new tier applies when it converts."
|
|
233
|
+
heading="Your plan"
|
|
234
|
+
footnote="Upgrade any time during your trial — your trial end date stays the same, and the new tier applies when it converts. To move to a lower tier, talk to sales."
|
|
231
235
|
/>
|
|
232
236
|
)}
|
|
233
237
|
</Flex>
|
package/src/billing/PlanGrid.jsx
CHANGED
|
@@ -54,12 +54,29 @@ function PlanCard({
|
|
|
54
54
|
// to this tier (the trial-archetype in-app upgrade) — no new sub.
|
|
55
55
|
endpoint = "checkout/start",
|
|
56
56
|
ctaLabel = "Choose",
|
|
57
|
+
// Trial-picker only: tier_order of the customer's current tier. When set, a
|
|
58
|
+
// card ranked BELOW it is a downgrade — shown for context but not self-serve
|
|
59
|
+
// (downgrades go through support), so it renders disabled with a Talk-to-sales
|
|
60
|
+
// CTA. Cards ranked ABOVE are upgrades (the normal CTA). null/undefined => the
|
|
61
|
+
// credit first-purchase grid, where this relation doesn't apply.
|
|
62
|
+
currentOrder = null,
|
|
57
63
|
}) {
|
|
58
64
|
// Default this card to annual when it offers annual (cheaper-per-month story);
|
|
59
65
|
// monthly-only tiers default to monthly. Both legs → show the toggle.
|
|
60
66
|
const hasMonthly = !!plan.monthly;
|
|
61
67
|
const hasAnnual = !!plan.annual;
|
|
62
|
-
|
|
68
|
+
// A lower-ranked tier than the customer's current one: a downgrade. Not
|
|
69
|
+
// self-serve (changes go through support) — show it for context, disabled,
|
|
70
|
+
// with a Talk-to-sales CTA. Only meaningful in the trial picker (currentOrder
|
|
71
|
+
// set); never flag the current tier itself as a downgrade.
|
|
72
|
+
const isDowngrade =
|
|
73
|
+
currentOrder != null &&
|
|
74
|
+
!plan.current &&
|
|
75
|
+
plan.tier_order != null &&
|
|
76
|
+
plan.tier_order < currentOrder;
|
|
77
|
+
// The interval toggle is only useful when the card can be acted on; a current
|
|
78
|
+
// or downgrade card is read-only, so hide its toggle.
|
|
79
|
+
const canToggle = hasMonthly && hasAnnual && !plan.current && !isDowngrade;
|
|
63
80
|
const [interval, setInterval] = useState(hasAnnual ? "annual" : "monthly");
|
|
64
81
|
const leg = interval === "annual" ? plan.annual : plan.monthly;
|
|
65
82
|
const periodLabel = interval === "annual" ? "/yr" : "/mo";
|
|
@@ -152,10 +169,20 @@ function PlanCard({
|
|
|
152
169
|
<Text key={`pad-${i}`}>{" "}</Text>
|
|
153
170
|
))}
|
|
154
171
|
|
|
155
|
-
{/* CTA. Current tier => disabled marker.
|
|
156
|
-
|
|
172
|
+
{/* CTA. Current tier => disabled marker. Downgrade (lower than current,
|
|
173
|
+
trial picker) => disabled + Talk-to-sales (not self-serve).
|
|
174
|
+
talk_to_sales tier => contact link. Otherwise pre-create-then-link
|
|
175
|
+
checkout/upgrade for the selected interval. */}
|
|
157
176
|
{plan.current ? (
|
|
158
177
|
<Button disabled>Current plan</Button>
|
|
178
|
+
) : isDowngrade ? (
|
|
179
|
+
<Button
|
|
180
|
+
href={supportUrl ? { url: supportUrl, external: true } : undefined}
|
|
181
|
+
disabled={!supportUrl}
|
|
182
|
+
variant="secondary"
|
|
183
|
+
>
|
|
184
|
+
Talk to sales to switch
|
|
185
|
+
</Button>
|
|
159
186
|
) : plan.talk_to_sales ? (
|
|
160
187
|
<Button
|
|
161
188
|
href={supportUrl ? { url: supportUrl, external: true } : undefined}
|
|
@@ -202,6 +229,9 @@ export function PlanGrid({
|
|
|
202
229
|
ctaLabel = "Choose",
|
|
203
230
|
heading = "Plans",
|
|
204
231
|
footnote = "Billing is managed in Stripe across all Growth Operations apps. Your new credits are added as soon as payment completes.",
|
|
232
|
+
// Trial-picker only: tier_order of the current tier, so cards below it render
|
|
233
|
+
// as disabled downgrades (Talk-to-sales). Omitted for the credit grid.
|
|
234
|
+
currentOrder = null,
|
|
205
235
|
}) {
|
|
206
236
|
const plans = plansOverride ?? state?.plans ?? [];
|
|
207
237
|
|
|
@@ -242,6 +272,7 @@ export function PlanGrid({
|
|
|
242
272
|
maxFeatures={maxFeatures}
|
|
243
273
|
endpoint={endpoint}
|
|
244
274
|
ctaLabel={ctaLabel}
|
|
275
|
+
currentOrder={currentOrder}
|
|
245
276
|
/>
|
|
246
277
|
))}
|
|
247
278
|
</AutoGrid>
|