@get-asset/sdk
LockedUpsell
Shared machinery behind every chart card's locked / upsell state. LockedUpsell has no Rootof its own — each lockable card's own Root accepts locked and upselland provides the context these parts read. Compose them inside that card's Locked state gate.
Preview
Profit & Loss
Anatomy
Import the primitive and compose the parts you need.
<ProfitLossCard.Root locked upsell={{ ctaLabel: "Upgrade now", onUpgrade }}><ProfitLossCard.Locked><LockedUpsell.Teaser>{/* the report's normal chart/bars/metrics markup, rendered as a blurred, inert teaser */}</LockedUpsell.Teaser><LockedUpsell.Overlay><LockedUpsell.Title /><LockedUpsell.Description /><LockedUpsell.Cta /></LockedUpsell.Overlay></ProfitLossCard.Locked></ProfitLossCard.Root>
API reference
Root props
| Prop | Type | Default | Description |
|---|---|---|---|
locked | boolean | — | On each lockable card's Root (default false). Gates the report for a viewer who isn't entitled to it — activates the Locked state (mutually exclusive with loading / error / empty / success) and skips the API fetch, including via retry(). Wins over the state override. |
upsell | { title?, description?, ctaLabel?, onUpgrade? } | — | On each lockable card's Root. Tenant-customizable copy + upgrade handler for the locked state. Resolved per field: the upsell prop, then the card's own default copy, then generic fallback copy. |
Parts
Parts
Compose these inside a lockable card's `Locked` state gate — they read the copy and upgrade handler from that card's Root.
| Part | Description |
|---|---|
LockedUpsell.TeaserasChild? | Wraps the report's normal markup as a blurred, inert teaser: aria-hidden, data-locked-teaser, pointer-events/user-select disabled, and the node set inert. |
LockedUpsell.OverlayasChild? | Wrapper for the lock badge, copy, and CTA. Renders role="group" with an aria-label of the resolved title. |
LockedUpsell.TitleasChild? | Resolved upsell headline. |
LockedUpsell.DescriptionasChild? | Resolved upsell disclaimer. |
LockedUpsell.CtaasChild? | Button that calls the tenant's onUpgrade on click. No-op when onUpgrade is omitted. |
Hooks
| Part | Description |
|---|---|
useLockedUpsell() | Hook returning { locked, upsell }, where upsell is the resolved { title, description, ctaLabel, onUpgrade }. Throws when used outside a lockable card's Root. |
Behavior notes
- No request fires while locked — including via
retry(), which is a no-op whenlockedis set (TanStack Query'srefetch()otherwise bypassesenabled). - With no data override, the card serves deterministic, built-in sample data as the teaser — healthy-looking and obviously generic. Passing a data override (e.g.
months) still wins over that sample data. - Unlocking is the tenant's responsibility: re-render with
lockedset tofalse. The SDK never auto-unlocks. lockedwins over thestateoverride — a card forced intostate="loading"(orerror/empty) still rendersLockedwhenlockedis also set.- Never pass real entitlement-gated data as the teaser — the teaser is meant to be a generic, illustrative sample, not the viewer's actual (gated) numbers.