Skip to content

dashboard: in-context upgrade prompts replacing generic CTAs (U2)#39

Merged
mastermanas805 merged 2 commits into
mainfrom
pricing/u2-context-prompts-fresh
May 12, 2026
Merged

dashboard: in-context upgrade prompts replacing generic CTAs (U2)#39
mastermanas805 merged 2 commits into
mainfrom
pricing/u2-context-prompts-fresh

Conversation

@mastermanas805
Copy link
Copy Markdown
Member

Summary

  • Every generic "Upgrade to Pro" CTA now renders a feature-specific UpgradePromptCard so the framing converts to the surface the user is on (vault prod isolation, family bindings, quota wall, custom domain, provision-twin).
  • Copy lives in one place (src/components/upgradeCopy.ts) keyed by UpgradeFeature. Each call site stays at one prop.
  • Card copy varies by surface; the CTA button inside it composes with P1's /auth/me.experiments.upgrade_cta variant. Falls back cleanly to DEFAULT_UPGRADE_CTA = "Upgrade to Pro →" when no variant is in flight.

Surfaces refactored (verified via grep audit)

  • DeployDetailPage.PromoteUpsell -> feature="family_bindings"
  • DeployDetailPage.CustomDomainUpsell -> feature="custom_domain"
  • CustomDomainPanel 402 banner -> feature="custom_domain" (dense)
  • ResourcesPage quota wall -> feature="quota_wall" (only fires for anonymous/free/hobby when any resource is at ≥ 80% storage)
  • VaultPage -> feature="vault_prod" (only fires when a single-env tier navigates to a non-prod env tab)
  • BillingPage -> keeps its generic primary CTA per brief; gains the data-testid="next-tier-unlocks" bullet list above the button so the user sees what their click pays for.

P1 coordination

P1's variant contract isn't published yet. Assumption (documented in upgradeCopy.ts and surfaced on the new AuthMeResponse.experiments field): the variant payload looks like { variant?: string; label?: string } and the UI only consumes label. If P1 ships a different shape we adapt the reader in UpgradePromptCard — call sites don't change.

Test plan

  • npm test: 234 pass, 3 skipped (was 209 / 3) — vitest
  • npx tsc --noEmit: clean
  • UpgradePromptCard unit tests (14 cases): copy per feature, CTA fallback, P1 variant, variantOverride, href
  • UpgradePromptSurfaces integration tests (9 cases): quota wall threshold, vault env gate, custom-domain 402, P1 composition
  • BillingPage gains 2 tests for the new "what unlocks" bullet panel

Out of scope

  • AppShell.tsx sidebar "Upgrade to Hobby" CTA — already feature-specific and targets a different tier (Hobby, not Pro). Left alone.

🤖 Generated with Claude Code

mastermanas805 and others added 2 commits May 12, 2026 23:51
Every generic "Upgrade to Pro" CTA now renders a feature-specific
UpgradePromptCard. Copy lives in src/components/upgradeCopy.ts as a
record map keyed by UpgradeFeature, so each call site stays at one
prop (<UpgradePromptCard feature="vault_prod" />) and the framing
varies by surface.

The CARD copy varies by surface; the BUTTON inside it composes with
P1's A/B variant on /auth/me.experiments.upgrade_cta. When no variant
is in flight we fall back to DEFAULT_UPGRADE_CTA so the UI degrades
cleanly.

Refactored surfaces (verified by grep audit):
- DeployDetailPage.PromoteUpsell -> feature="family_bindings"
- DeployDetailPage.CustomDomainUpsell -> feature="custom_domain"
- CustomDomainPanel 402 banner -> feature="custom_domain" (dense)
- ResourcesPage quota wall -> feature="quota_wall" (>=80% on hobby)
- VaultPage non-prod env on hobby -> feature="vault_prod"
- BillingPage main CTA stays generic, gains "what <next> unlocks"
  bullet list above the button (per brief).

23 new tests across UpgradePromptCard.test.tsx + UpgradePromptSurfaces.test.tsx
+ BillingPage.test.tsx. 234 pass / 3 skip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ompts-fresh

# Conflicts:
#	src/pages/BillingPage.tsx
@mastermanas805 mastermanas805 merged commit 4e61cb3 into main May 12, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant