@churnkey/react 0.6.0 → 0.6.2
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/dist/{chunk-YKDJMCSW.cjs → chunk-224XOEYJ.cjs} +6 -6
- package/dist/{chunk-YKDJMCSW.cjs.map → chunk-224XOEYJ.cjs.map} +1 -1
- package/dist/{chunk-G57YLYM5.js → chunk-EYIGZLVT.js} +3 -74
- package/dist/chunk-EYIGZLVT.js.map +1 -0
- package/dist/{chunk-4FGFYJPW.js → chunk-J6BBPG6T.cjs} +88 -3
- package/dist/chunk-J6BBPG6T.cjs.map +1 -0
- package/dist/{chunk-IXGAI4LH.cjs → chunk-KS2T4QBH.js} +77 -7
- package/dist/chunk-KS2T4QBH.js.map +1 -0
- package/dist/{chunk-BDBZ5OS3.cjs → chunk-WVSNF3TN.cjs} +2 -78
- package/dist/chunk-WVSNF3TN.cjs.map +1 -0
- package/dist/{chunk-DJ5AAFK2.js → chunk-ZIH7TTIZ.js} +3 -3
- package/dist/{chunk-DJ5AAFK2.js.map → chunk-ZIH7TTIZ.js.map} +1 -1
- package/dist/core.cjs +26 -26
- package/dist/core.d.cts +324 -0
- package/dist/core.d.ts +324 -0
- package/dist/core.js +2 -2
- package/dist/headless.cjs +3 -3
- package/dist/headless.d.cts +34 -0
- package/dist/headless.d.ts +34 -0
- package/dist/headless.js +2 -2
- package/dist/index.cjs +120 -126
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.js +8 -14
- package/dist/index.js.map +1 -1
- package/dist/step-graph-BpoAhd0d.d.cts +650 -0
- package/dist/step-graph-BpoAhd0d.d.ts +650 -0
- package/dist/styles.css.d.ts +1 -0
- package/package.json +1 -1
- package/dist/chunk-4FGFYJPW.js.map +0 -1
- package/dist/chunk-BDBZ5OS3.cjs.map +0 -1
- package/dist/chunk-G57YLYM5.js.map +0 -1
- package/dist/chunk-IXGAI4LH.cjs.map +0 -1
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
import { ReactNode, ReactElement, ComponentType } from 'react';
|
|
2
|
+
|
|
3
|
+
interface DirectAddress {
|
|
4
|
+
line1?: string;
|
|
5
|
+
line2?: string;
|
|
6
|
+
city?: string;
|
|
7
|
+
state?: string;
|
|
8
|
+
postalCode?: string;
|
|
9
|
+
/** ISO 3166-1 alpha-2 */
|
|
10
|
+
country?: string;
|
|
11
|
+
}
|
|
12
|
+
interface DirectCustomer {
|
|
13
|
+
id: string;
|
|
14
|
+
email?: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
lastName?: string;
|
|
17
|
+
phone?: string;
|
|
18
|
+
/** ISO 4217 */
|
|
19
|
+
currency?: string;
|
|
20
|
+
addresses?: DirectAddress[];
|
|
21
|
+
metadata?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
interface DirectPrice {
|
|
24
|
+
id: string;
|
|
25
|
+
type?: 'standalone' | 'product';
|
|
26
|
+
active?: boolean;
|
|
27
|
+
productId?: string;
|
|
28
|
+
name?: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
duration?: {
|
|
31
|
+
interval: 'day' | 'week' | 'month' | 'year';
|
|
32
|
+
intervalCount?: number;
|
|
33
|
+
};
|
|
34
|
+
amount: {
|
|
35
|
+
model?: 'fixed' | 'tiered';
|
|
36
|
+
/** Smallest currency unit (cents for USD). */
|
|
37
|
+
value: number;
|
|
38
|
+
currency?: string;
|
|
39
|
+
};
|
|
40
|
+
metadata?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
interface DirectCoupon {
|
|
43
|
+
id?: string;
|
|
44
|
+
name?: string;
|
|
45
|
+
percentOff?: number;
|
|
46
|
+
/** Smallest currency unit (cents for USD). */
|
|
47
|
+
amountOff?: number;
|
|
48
|
+
currency?: string;
|
|
49
|
+
duration?: 'once' | 'repeating' | 'forever';
|
|
50
|
+
durationInMonths?: number;
|
|
51
|
+
metadata?: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
type SubscriptionStatus = {
|
|
54
|
+
name: 'active';
|
|
55
|
+
currentPeriod: {
|
|
56
|
+
start: Date | string;
|
|
57
|
+
end: Date | string;
|
|
58
|
+
};
|
|
59
|
+
} | {
|
|
60
|
+
name: 'trial';
|
|
61
|
+
trial: {
|
|
62
|
+
start: Date | string;
|
|
63
|
+
end: Date | string;
|
|
64
|
+
};
|
|
65
|
+
currentPeriod?: {
|
|
66
|
+
start: Date | string;
|
|
67
|
+
end: Date | string;
|
|
68
|
+
};
|
|
69
|
+
} | {
|
|
70
|
+
name: 'paused';
|
|
71
|
+
pause: {
|
|
72
|
+
start: Date | string;
|
|
73
|
+
end?: Date | string;
|
|
74
|
+
};
|
|
75
|
+
currentPeriod?: {
|
|
76
|
+
start: Date | string;
|
|
77
|
+
end: Date | string;
|
|
78
|
+
};
|
|
79
|
+
} | {
|
|
80
|
+
name: 'canceled';
|
|
81
|
+
canceledAt: Date | string;
|
|
82
|
+
} | {
|
|
83
|
+
name: 'unpaid';
|
|
84
|
+
currentPeriod?: {
|
|
85
|
+
start: Date | string;
|
|
86
|
+
end: Date | string;
|
|
87
|
+
};
|
|
88
|
+
} | {
|
|
89
|
+
name: 'future';
|
|
90
|
+
currentPeriod?: {
|
|
91
|
+
start: Date | string;
|
|
92
|
+
end: Date | string;
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
interface DirectSubscriptionItem {
|
|
96
|
+
id?: string;
|
|
97
|
+
price: DirectPrice;
|
|
98
|
+
quantity?: number;
|
|
99
|
+
}
|
|
100
|
+
interface DirectSubscription {
|
|
101
|
+
id: string;
|
|
102
|
+
customerId?: string;
|
|
103
|
+
start: Date | string;
|
|
104
|
+
status: SubscriptionStatus;
|
|
105
|
+
items: DirectSubscriptionItem[];
|
|
106
|
+
duration?: {
|
|
107
|
+
interval: 'day' | 'week' | 'month' | 'year';
|
|
108
|
+
intervalCount?: number;
|
|
109
|
+
};
|
|
110
|
+
end?: Date | string;
|
|
111
|
+
discounts?: Array<{
|
|
112
|
+
id?: string;
|
|
113
|
+
coupon?: DirectCoupon;
|
|
114
|
+
start?: Date | string;
|
|
115
|
+
end?: Date | string;
|
|
116
|
+
}>;
|
|
117
|
+
metadata?: Record<string, unknown>;
|
|
118
|
+
}
|
|
119
|
+
interface DiscountOffer {
|
|
120
|
+
type: 'discount';
|
|
121
|
+
couponId?: string;
|
|
122
|
+
percentOff?: number;
|
|
123
|
+
/** Smallest currency unit (cents for USD). */
|
|
124
|
+
amountOff?: number;
|
|
125
|
+
currency?: string;
|
|
126
|
+
durationInMonths?: number;
|
|
127
|
+
}
|
|
128
|
+
interface PauseOffer {
|
|
129
|
+
type: 'pause';
|
|
130
|
+
months: number;
|
|
131
|
+
interval?: 'month' | 'week';
|
|
132
|
+
}
|
|
133
|
+
interface PlanChangeOffer {
|
|
134
|
+
type: 'plan_change';
|
|
135
|
+
plans: PlanOption[];
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Plan option in a `plan_change` offer — `DirectPrice` plus optional
|
|
139
|
+
* cancel-flow merchandising fields. Marketing fields are presentation-only
|
|
140
|
+
* and live on `PlanOption` rather than `DirectPrice` so `Direct` stays a
|
|
141
|
+
* clean billing-data shape reusable outside the cancel-flow context.
|
|
142
|
+
*/
|
|
143
|
+
interface PlanOption extends DirectPrice {
|
|
144
|
+
/** Short marketing line (e.g. "Most popular"). */
|
|
145
|
+
tagline?: string;
|
|
146
|
+
/** Bullet list of plan features shown on the card. */
|
|
147
|
+
features?: string[];
|
|
148
|
+
/** Pre-formatted "before" price rendered struck-through (e.g. "$49/mo"). */
|
|
149
|
+
msrp?: string;
|
|
150
|
+
}
|
|
151
|
+
interface TrialExtensionOffer {
|
|
152
|
+
type: 'trial_extension';
|
|
153
|
+
days: number;
|
|
154
|
+
}
|
|
155
|
+
interface ContactOffer {
|
|
156
|
+
type: 'contact';
|
|
157
|
+
url?: string;
|
|
158
|
+
label?: string;
|
|
159
|
+
}
|
|
160
|
+
interface RedirectOffer {
|
|
161
|
+
type: 'redirect';
|
|
162
|
+
url: string;
|
|
163
|
+
label: string;
|
|
164
|
+
}
|
|
165
|
+
interface RebateOffer {
|
|
166
|
+
type: 'rebate';
|
|
167
|
+
/** The rebate amount (pre-tax), smallest currency unit. The card is refunded this plus any tax charged on it. */
|
|
168
|
+
amountMinor: number;
|
|
169
|
+
currency: string;
|
|
170
|
+
/** Gross paid on the target invoice. Server-resolved in token mode. */
|
|
171
|
+
amountPaidMinor?: number;
|
|
172
|
+
/** amountPaidMinor minus the full refund (the rebate plus its tax). Server-resolved in token mode. */
|
|
173
|
+
netAfterRebateMinor?: number;
|
|
174
|
+
paymentMethodBrand?: string;
|
|
175
|
+
paymentMethodLast4?: string;
|
|
176
|
+
}
|
|
177
|
+
interface CustomOfferConfig {
|
|
178
|
+
type: string;
|
|
179
|
+
data?: Record<string, unknown>;
|
|
180
|
+
}
|
|
181
|
+
type BuiltInOfferConfig = DiscountOffer | PauseOffer | PlanChangeOffer | TrialExtensionOffer | ContactOffer | RedirectOffer | RebateOffer;
|
|
182
|
+
type OfferConfig = BuiltInOfferConfig | CustomOfferConfig;
|
|
183
|
+
type OfferDecision = OfferConfig & {
|
|
184
|
+
copy: OfferCopy;
|
|
185
|
+
decisionId?: string;
|
|
186
|
+
};
|
|
187
|
+
interface OfferCopy {
|
|
188
|
+
headline: string;
|
|
189
|
+
/**
|
|
190
|
+
* May contain HTML when the flow was authored in the Churnkey dashboard
|
|
191
|
+
* (the description editor is rich text). Render with the exported
|
|
192
|
+
* `RichText` component — the same renderer the built-in steps use — rather
|
|
193
|
+
* than as a text node, or the markup shows escaped.
|
|
194
|
+
*/
|
|
195
|
+
body: string;
|
|
196
|
+
cta: string;
|
|
197
|
+
declineCta: string;
|
|
198
|
+
}
|
|
199
|
+
type AcceptedOffer = OfferConfig & {
|
|
200
|
+
/** Survey reason that routed to this offer. Absent when the offer was
|
|
201
|
+
* declared as a standalone `OfferStep`. */
|
|
202
|
+
reasonId?: string;
|
|
203
|
+
/** Payload from custom offers — whatever your component passed to
|
|
204
|
+
* `onAccept(result)`. Built-in offer types do not populate this. */
|
|
205
|
+
result?: Record<string, unknown>;
|
|
206
|
+
};
|
|
207
|
+
interface ReasonConfig {
|
|
208
|
+
id: string;
|
|
209
|
+
label: string;
|
|
210
|
+
/**
|
|
211
|
+
* When true, picking this reason reveals a text input below the reason list.
|
|
212
|
+
* The typed text lands on the session as `followupResponse`. `surveyChoiceId`
|
|
213
|
+
* still carries the reason's `id` and `surveyChoiceValue` still carries the
|
|
214
|
+
* static `label`, so analytics groupings stay stable.
|
|
215
|
+
*/
|
|
216
|
+
freeform?: boolean;
|
|
217
|
+
offer?: OfferConfig;
|
|
218
|
+
}
|
|
219
|
+
interface SurveyStep {
|
|
220
|
+
type: 'survey';
|
|
221
|
+
guid?: string;
|
|
222
|
+
title?: string;
|
|
223
|
+
description?: string;
|
|
224
|
+
reasons: ReasonConfig[];
|
|
225
|
+
classNames?: SurveyClassNames;
|
|
226
|
+
}
|
|
227
|
+
interface OfferStep {
|
|
228
|
+
type: 'offer';
|
|
229
|
+
guid?: string;
|
|
230
|
+
title?: string;
|
|
231
|
+
description?: string;
|
|
232
|
+
/**
|
|
233
|
+
* Offer attached to this step. Set this to declare a standalone offer
|
|
234
|
+
* step (one that isn't routed from a survey reason). `copy` is optional —
|
|
235
|
+
* the SDK synthesizes default copy from the offer config when none is
|
|
236
|
+
* provided, the same way it does for survey-attached offers.
|
|
237
|
+
*/
|
|
238
|
+
offer?: OfferConfig | OfferDecision;
|
|
239
|
+
classNames?: OfferClassNames;
|
|
240
|
+
}
|
|
241
|
+
interface FeedbackStep {
|
|
242
|
+
type: 'feedback';
|
|
243
|
+
guid?: string;
|
|
244
|
+
title?: string;
|
|
245
|
+
description?: string;
|
|
246
|
+
placeholder?: string;
|
|
247
|
+
required?: boolean;
|
|
248
|
+
minLength?: number;
|
|
249
|
+
classNames?: FeedbackClassNames;
|
|
250
|
+
}
|
|
251
|
+
interface ConfirmStep {
|
|
252
|
+
type: 'confirm';
|
|
253
|
+
guid?: string;
|
|
254
|
+
title?: string;
|
|
255
|
+
description?: string;
|
|
256
|
+
/** Optional bullet list of what the customer is giving up. Rendered between the description and the period-end notice. */
|
|
257
|
+
losses?: string[];
|
|
258
|
+
/** Heading above the loss list. Defaults to "You'll lose access to:". */
|
|
259
|
+
lossesLabel?: string;
|
|
260
|
+
confirmLabel?: string;
|
|
261
|
+
goBackLabel?: string;
|
|
262
|
+
classNames?: ConfirmClassNames;
|
|
263
|
+
}
|
|
264
|
+
interface SuccessStep {
|
|
265
|
+
type: 'success';
|
|
266
|
+
guid?: string;
|
|
267
|
+
savedTitle?: string;
|
|
268
|
+
savedDescription?: string;
|
|
269
|
+
cancelledTitle?: string;
|
|
270
|
+
cancelledDescription?: string;
|
|
271
|
+
classNames?: SuccessClassNames;
|
|
272
|
+
}
|
|
273
|
+
interface CustomStepConfig {
|
|
274
|
+
type: string;
|
|
275
|
+
guid?: string;
|
|
276
|
+
title?: string;
|
|
277
|
+
description?: string;
|
|
278
|
+
data?: Record<string, unknown>;
|
|
279
|
+
}
|
|
280
|
+
type BuiltInStep = SurveyStep | OfferStep | FeedbackStep | ConfirmStep | SuccessStep;
|
|
281
|
+
type Step = BuiltInStep | CustomStepConfig;
|
|
282
|
+
type BuiltInStepType = 'survey' | 'offer' | 'feedback' | 'confirm' | 'success';
|
|
283
|
+
interface SurveyClassNames {
|
|
284
|
+
root?: string;
|
|
285
|
+
title?: string;
|
|
286
|
+
description?: string;
|
|
287
|
+
reasonList?: string;
|
|
288
|
+
reasonButton?: string;
|
|
289
|
+
reasonButtonSelected?: string;
|
|
290
|
+
reasonLabel?: string;
|
|
291
|
+
followupInput?: string;
|
|
292
|
+
continueButton?: string;
|
|
293
|
+
}
|
|
294
|
+
interface OfferClassNames {
|
|
295
|
+
root?: string;
|
|
296
|
+
title?: string;
|
|
297
|
+
description?: string;
|
|
298
|
+
card?: string;
|
|
299
|
+
headline?: string;
|
|
300
|
+
body?: string;
|
|
301
|
+
acceptButton?: string;
|
|
302
|
+
declineButton?: string;
|
|
303
|
+
discountBadge?: string;
|
|
304
|
+
priceComparison?: string;
|
|
305
|
+
pauseSlider?: string;
|
|
306
|
+
planGrid?: string;
|
|
307
|
+
planCard?: string;
|
|
308
|
+
planCardSelected?: string;
|
|
309
|
+
}
|
|
310
|
+
interface FeedbackClassNames {
|
|
311
|
+
root?: string;
|
|
312
|
+
title?: string;
|
|
313
|
+
description?: string;
|
|
314
|
+
textarea?: string;
|
|
315
|
+
characterCount?: string;
|
|
316
|
+
submitButton?: string;
|
|
317
|
+
}
|
|
318
|
+
interface ConfirmClassNames {
|
|
319
|
+
root?: string;
|
|
320
|
+
title?: string;
|
|
321
|
+
description?: string;
|
|
322
|
+
lossList?: string;
|
|
323
|
+
lossLabel?: string;
|
|
324
|
+
lossItem?: string;
|
|
325
|
+
lossBullet?: string;
|
|
326
|
+
confirmButton?: string;
|
|
327
|
+
goBackButton?: string;
|
|
328
|
+
periodEndNotice?: string;
|
|
329
|
+
}
|
|
330
|
+
interface SuccessClassNames {
|
|
331
|
+
root?: string;
|
|
332
|
+
icon?: string;
|
|
333
|
+
title?: string;
|
|
334
|
+
description?: string;
|
|
335
|
+
closeButton?: string;
|
|
336
|
+
}
|
|
337
|
+
interface StructuralClassNames {
|
|
338
|
+
overlay?: string;
|
|
339
|
+
modal?: string;
|
|
340
|
+
closeButton?: string;
|
|
341
|
+
backButton?: string;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Typed surface for `appearance.variables`. Every key maps to a `--ck-*`
|
|
345
|
+
* CSS custom property. Consumers who need a token not in this list can
|
|
346
|
+
* still set the underlying CSS variable directly — these are the ones
|
|
347
|
+
* exposed through the typed JS API.
|
|
348
|
+
*/
|
|
349
|
+
interface AppearanceVariables {
|
|
350
|
+
colorBackground: string;
|
|
351
|
+
colorSurface: string;
|
|
352
|
+
colorSurfaceMuted: string;
|
|
353
|
+
colorBorder: string;
|
|
354
|
+
colorBorderStrong: string;
|
|
355
|
+
colorText: string;
|
|
356
|
+
colorTextSecondary: string;
|
|
357
|
+
colorTextMuted: string;
|
|
358
|
+
colorPrimary: string;
|
|
359
|
+
colorPrimaryHover: string;
|
|
360
|
+
colorPrimarySoft: string;
|
|
361
|
+
colorSuccess: string;
|
|
362
|
+
colorSuccessSoft: string;
|
|
363
|
+
colorDanger: string;
|
|
364
|
+
colorDangerHover: string;
|
|
365
|
+
colorDangerSoft: string;
|
|
366
|
+
fontFamily: string;
|
|
367
|
+
fontFamilyMono: string;
|
|
368
|
+
/**
|
|
369
|
+
* Display face used by step titles and other visual headlines. Defaults
|
|
370
|
+
* to `fontFamily`. Set this when your brand uses a separate display face
|
|
371
|
+
* (Tiempos, Canela, etc.) for headings.
|
|
372
|
+
*/
|
|
373
|
+
fontFamilyDisplay: string;
|
|
374
|
+
fontSize: string;
|
|
375
|
+
/** Weight applied to the step title. Default `'600'`. */
|
|
376
|
+
fontWeightDisplay: string;
|
|
377
|
+
/** Letter spacing applied to the step title. Default `'-0.015em'`. */
|
|
378
|
+
letterSpacingDisplay: string;
|
|
379
|
+
borderRadius: string;
|
|
380
|
+
radiusSm: string;
|
|
381
|
+
radiusMd: string;
|
|
382
|
+
radiusLg: string;
|
|
383
|
+
radiusXl: string;
|
|
384
|
+
shadowModal: string;
|
|
385
|
+
shadowCard: string;
|
|
386
|
+
/**
|
|
387
|
+
* Color of the dim behind the modal. Defaults to a neutral translucent
|
|
388
|
+
* ink. Set to `color-mix(in srgb, var(--ck-color-primary) 40%, transparent)`
|
|
389
|
+
* to derive the overlay from your primary color, or any CSS color value.
|
|
390
|
+
*/
|
|
391
|
+
overlayColor: string;
|
|
392
|
+
}
|
|
393
|
+
interface Appearance {
|
|
394
|
+
/**
|
|
395
|
+
* `'auto'` follows the user's OS preference (`prefers-color-scheme`) and
|
|
396
|
+
* watches for changes. Default: `'light'`.
|
|
397
|
+
*/
|
|
398
|
+
colorScheme?: 'auto' | 'light' | 'dark';
|
|
399
|
+
variables?: Partial<AppearanceVariables>;
|
|
400
|
+
}
|
|
401
|
+
interface CustomStepProps {
|
|
402
|
+
step: CustomStepConfig;
|
|
403
|
+
customer: DirectCustomer | null;
|
|
404
|
+
subscriptions: DirectSubscription[];
|
|
405
|
+
onNext: (result?: Record<string, unknown>) => void;
|
|
406
|
+
onBack: () => void;
|
|
407
|
+
}
|
|
408
|
+
interface CustomOfferProps {
|
|
409
|
+
offer: OfferDecision;
|
|
410
|
+
customer: DirectCustomer | null;
|
|
411
|
+
subscriptions: DirectSubscription[];
|
|
412
|
+
onAccept: (result?: Record<string, unknown>) => Promise<void>;
|
|
413
|
+
onDecline: () => void;
|
|
414
|
+
isProcessing: boolean;
|
|
415
|
+
}
|
|
416
|
+
interface ComponentOverrides {
|
|
417
|
+
Modal?: (props: ModalProps) => ReactElement;
|
|
418
|
+
CloseButton?: (props: CloseButtonProps) => ReactElement;
|
|
419
|
+
BackButton?: (props: BackButtonProps) => ReactElement;
|
|
420
|
+
Survey?: (props: SurveyStepProps) => ReactElement;
|
|
421
|
+
Offer?: (props: OfferStepProps) => ReactElement;
|
|
422
|
+
Feedback?: (props: FeedbackStepProps) => ReactElement;
|
|
423
|
+
Confirm?: (props: ConfirmStepProps) => ReactElement;
|
|
424
|
+
Success?: (props: SuccessStepProps) => ReactElement;
|
|
425
|
+
ReasonButton?: (props: ReasonButtonProps) => ReactElement;
|
|
426
|
+
DiscountOffer?: (props: OfferStepProps) => ReactElement;
|
|
427
|
+
PauseOffer?: (props: OfferStepProps) => ReactElement;
|
|
428
|
+
PlanChangeOffer?: (props: OfferStepProps) => ReactElement;
|
|
429
|
+
TrialExtensionOffer?: (props: OfferStepProps) => ReactElement;
|
|
430
|
+
ContactOffer?: (props: OfferStepProps) => ReactElement;
|
|
431
|
+
RedirectOffer?: (props: OfferStepProps) => ReactElement;
|
|
432
|
+
RebateOffer?: (props: OfferStepProps) => ReactElement;
|
|
433
|
+
}
|
|
434
|
+
type CustomComponents = Record<string, ComponentType<CustomStepProps> | ComponentType<CustomOfferProps>>;
|
|
435
|
+
interface ModalProps {
|
|
436
|
+
open: boolean;
|
|
437
|
+
onClose: () => void;
|
|
438
|
+
children: ReactNode;
|
|
439
|
+
className?: string;
|
|
440
|
+
overlayClassName?: string;
|
|
441
|
+
}
|
|
442
|
+
interface CloseButtonProps {
|
|
443
|
+
onClose: () => void;
|
|
444
|
+
className?: string;
|
|
445
|
+
}
|
|
446
|
+
interface BackButtonProps {
|
|
447
|
+
onBack: () => void;
|
|
448
|
+
className?: string;
|
|
449
|
+
}
|
|
450
|
+
interface SurveyStepProps {
|
|
451
|
+
title: string;
|
|
452
|
+
description?: string;
|
|
453
|
+
customer: DirectCustomer | null;
|
|
454
|
+
subscriptions: DirectSubscription[];
|
|
455
|
+
reasons: ReasonConfig[];
|
|
456
|
+
selectedReason: string | null;
|
|
457
|
+
onSelectReason: (id: string) => void;
|
|
458
|
+
/** Free-text value when the selected reason has `freeform: true`. Lands on the session as `followupResponse`. */
|
|
459
|
+
followupResponse: string;
|
|
460
|
+
/** Set the follow-up response. The SDK forwards the value to the session. */
|
|
461
|
+
onFollowupResponseChange: (text: string) => void;
|
|
462
|
+
onNext: () => void;
|
|
463
|
+
classNames?: SurveyClassNames;
|
|
464
|
+
components?: Partial<ComponentOverrides>;
|
|
465
|
+
}
|
|
466
|
+
interface OfferStepProps {
|
|
467
|
+
title?: string;
|
|
468
|
+
description?: string;
|
|
469
|
+
customer: DirectCustomer | null;
|
|
470
|
+
subscriptions: DirectSubscription[];
|
|
471
|
+
offer: OfferDecision;
|
|
472
|
+
/**
|
|
473
|
+
* Accept the offer. The optional `result` is included on the resulting
|
|
474
|
+
* `AcceptedOffer` payload — used by offers that need a user choice (e.g.
|
|
475
|
+
* `plan_change` passes `{ planId }`).
|
|
476
|
+
*/
|
|
477
|
+
onAccept: (result?: Record<string, unknown>) => Promise<void>;
|
|
478
|
+
onDecline: () => void;
|
|
479
|
+
isProcessing: boolean;
|
|
480
|
+
classNames?: OfferClassNames;
|
|
481
|
+
/**
|
|
482
|
+
* Forwarded to the default `Offer` switcher so it can dispatch to per-type
|
|
483
|
+
* overrides (`DiscountOffer`, `PauseOffer`, etc). Custom `Offer`
|
|
484
|
+
* implementations can ignore this.
|
|
485
|
+
*/
|
|
486
|
+
components?: Partial<ComponentOverrides>;
|
|
487
|
+
}
|
|
488
|
+
interface FeedbackStepProps {
|
|
489
|
+
title: string;
|
|
490
|
+
description?: string;
|
|
491
|
+
customer: DirectCustomer | null;
|
|
492
|
+
subscriptions: DirectSubscription[];
|
|
493
|
+
placeholder?: string;
|
|
494
|
+
required: boolean;
|
|
495
|
+
minLength: number;
|
|
496
|
+
value: string;
|
|
497
|
+
onChange: (text: string) => void;
|
|
498
|
+
onSubmit: () => void;
|
|
499
|
+
classNames?: FeedbackClassNames;
|
|
500
|
+
}
|
|
501
|
+
interface ConfirmStepProps {
|
|
502
|
+
title: string;
|
|
503
|
+
description?: string;
|
|
504
|
+
customer: DirectCustomer | null;
|
|
505
|
+
subscriptions: DirectSubscription[];
|
|
506
|
+
losses?: string[];
|
|
507
|
+
lossesLabel?: string;
|
|
508
|
+
confirmLabel: string;
|
|
509
|
+
goBackLabel: string;
|
|
510
|
+
onConfirm: () => Promise<void>;
|
|
511
|
+
onGoBack: () => void;
|
|
512
|
+
isProcessing: boolean;
|
|
513
|
+
classNames?: ConfirmClassNames;
|
|
514
|
+
}
|
|
515
|
+
interface SuccessStepProps {
|
|
516
|
+
outcome: 'saved' | 'cancelled';
|
|
517
|
+
offer?: OfferDecision;
|
|
518
|
+
title: string;
|
|
519
|
+
description?: string;
|
|
520
|
+
customer: DirectCustomer | null;
|
|
521
|
+
subscriptions: DirectSubscription[];
|
|
522
|
+
onClose: () => void;
|
|
523
|
+
classNames?: SuccessClassNames;
|
|
524
|
+
}
|
|
525
|
+
interface ReasonButtonProps {
|
|
526
|
+
reason: ReasonConfig;
|
|
527
|
+
index: number;
|
|
528
|
+
isSelected: boolean;
|
|
529
|
+
onSelect: (id: string) => void;
|
|
530
|
+
}
|
|
531
|
+
interface FlowState {
|
|
532
|
+
step: string;
|
|
533
|
+
currentStepId: string;
|
|
534
|
+
selectedReason: string | null;
|
|
535
|
+
followupResponse: string;
|
|
536
|
+
feedback: string;
|
|
537
|
+
outcome: 'saved' | 'cancelled' | null;
|
|
538
|
+
isProcessing: boolean;
|
|
539
|
+
error: Error | null;
|
|
540
|
+
customer: DirectCustomer | null;
|
|
541
|
+
subscriptions: DirectSubscription[];
|
|
542
|
+
}
|
|
543
|
+
declare const MODES: readonly ["live", "test", "sandbox"];
|
|
544
|
+
/**
|
|
545
|
+
* Environment a session runs against. `'test'` and `'sandbox'` both keep
|
|
546
|
+
* sessions out of live analytics; `'sandbox'` additionally routes server-side
|
|
547
|
+
* billing actions to the org's Stripe Sandbox credentials, which live under a
|
|
548
|
+
* separate Stripe account ID from live/test mode.
|
|
549
|
+
*/
|
|
550
|
+
type Mode = (typeof MODES)[number];
|
|
551
|
+
interface FlowConfig extends FlowCallbacks {
|
|
552
|
+
appId?: string;
|
|
553
|
+
customer?: DirectCustomer;
|
|
554
|
+
subscriptions?: DirectSubscription[];
|
|
555
|
+
/**
|
|
556
|
+
* Client-side attribute layer on top of provider data, e.g. usage counts
|
|
557
|
+
* or entitlements only the host app knows (`{ videosCreated: 28 }`). In token mode
|
|
558
|
+
* they're sent with the config request so segments can match on them when
|
|
559
|
+
* picking the blueprint. In every mode they resolve as merge fields and
|
|
560
|
+
* are recorded on the session, taking precedence over `customer.metadata`
|
|
561
|
+
* keys. Same role as the embed's `customerAttributes`.
|
|
562
|
+
*/
|
|
563
|
+
customerAttributes?: Record<string, unknown>;
|
|
564
|
+
session?: string;
|
|
565
|
+
apiBaseUrl?: string;
|
|
566
|
+
steps?: Step[];
|
|
567
|
+
/**
|
|
568
|
+
* Tags the session as live, test, or sandbox. Use `'test'` from staging so
|
|
569
|
+
* your dashboard can filter out non-production traffic, or `'sandbox'` when
|
|
570
|
+
* the org is connected to a Stripe Sandbox. Defaults to `'live'`.
|
|
571
|
+
* In token mode the mode is encoded in the signed token and overrides
|
|
572
|
+
* this field.
|
|
573
|
+
*/
|
|
574
|
+
mode?: Mode;
|
|
575
|
+
}
|
|
576
|
+
type OfferCallback = (offer: AcceptedOffer, customer: DirectCustomer | null) => Promise<void> | void;
|
|
577
|
+
type CancelCallback = (customer: DirectCustomer | null) => Promise<void> | void;
|
|
578
|
+
/**
|
|
579
|
+
* Two kinds of callbacks, distinguished by name:
|
|
580
|
+
*
|
|
581
|
+
* - `handle<Type>` runs the action. When defined, it replaces whatever
|
|
582
|
+
* Churnkey would do on the server — the consumer takes responsibility.
|
|
583
|
+
* In local mode (no token), handlers are the only path that does anything.
|
|
584
|
+
* - `on<Type>` is a listener that fires after the action. Side effects only —
|
|
585
|
+
* refetch state, log analytics, show a toast. Errors thrown here are
|
|
586
|
+
* swallowed; listeners can't flip the flow into an error state.
|
|
587
|
+
*
|
|
588
|
+
* `onAccept` is a catch-all that fires alongside the per-type listener.
|
|
589
|
+
*/
|
|
590
|
+
interface FlowCallbacks {
|
|
591
|
+
handleDiscount?: OfferCallback;
|
|
592
|
+
handlePause?: OfferCallback;
|
|
593
|
+
handlePlanChange?: OfferCallback;
|
|
594
|
+
handleTrialExtension?: OfferCallback;
|
|
595
|
+
handleRebate?: OfferCallback;
|
|
596
|
+
handleCancel?: CancelCallback;
|
|
597
|
+
onAccept?: OfferCallback;
|
|
598
|
+
onDiscount?: OfferCallback;
|
|
599
|
+
onPause?: OfferCallback;
|
|
600
|
+
onPlanChange?: OfferCallback;
|
|
601
|
+
onTrialExtension?: OfferCallback;
|
|
602
|
+
onRebate?: OfferCallback;
|
|
603
|
+
onCancel?: CancelCallback;
|
|
604
|
+
onClose?: () => void;
|
|
605
|
+
onStepChange?: (step: string, prevStep: string) => void;
|
|
606
|
+
}
|
|
607
|
+
interface CancelFlowProps extends FlowCallbacks {
|
|
608
|
+
appId?: string;
|
|
609
|
+
customer?: DirectCustomer;
|
|
610
|
+
subscriptions?: DirectSubscription[];
|
|
611
|
+
/** See FlowConfig.customerAttributes. */
|
|
612
|
+
customerAttributes?: Record<string, unknown>;
|
|
613
|
+
session?: string;
|
|
614
|
+
steps?: Step[];
|
|
615
|
+
apiBaseUrl?: string;
|
|
616
|
+
/** See FlowConfig.mode. Ignored in token mode (the token is authoritative). */
|
|
617
|
+
mode?: Mode;
|
|
618
|
+
appearance?: Appearance;
|
|
619
|
+
classNames?: StructuralClassNames;
|
|
620
|
+
components?: Partial<ComponentOverrides>;
|
|
621
|
+
customComponents?: CustomComponents;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
interface ResolvedStep {
|
|
625
|
+
guid: string;
|
|
626
|
+
type: string;
|
|
627
|
+
defaultNextStep?: string;
|
|
628
|
+
defaultPreviousStep?: string;
|
|
629
|
+
title?: string;
|
|
630
|
+
description?: string;
|
|
631
|
+
reasons?: ReasonConfig[];
|
|
632
|
+
offersAttached?: Record<string, string>;
|
|
633
|
+
numChoices?: number;
|
|
634
|
+
offer?: OfferDecision;
|
|
635
|
+
/** True if this step was synthesized from a survey choice. Used by stepIndex so synthetic offers share the survey's progress slot. */
|
|
636
|
+
surveyOffer?: boolean;
|
|
637
|
+
placeholder?: string;
|
|
638
|
+
required?: boolean;
|
|
639
|
+
minLength?: number;
|
|
640
|
+
losses?: string[];
|
|
641
|
+
lossesLabel?: string;
|
|
642
|
+
savedTitle?: string;
|
|
643
|
+
savedDescription?: string;
|
|
644
|
+
cancelledTitle?: string;
|
|
645
|
+
cancelledDescription?: string;
|
|
646
|
+
data?: Record<string, unknown>;
|
|
647
|
+
classNames?: unknown;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
export type { SuccessStep as $, AcceptedOffer as A, BackButtonProps as B, CancelFlowProps as C, DirectAddress as D, FlowState as E, FeedbackStepProps as F, Mode as G, OfferClassNames as H, OfferConfig as I, OfferCopy as J, OfferDecision as K, OfferStep as L, ModalProps as M, PlanChangeOffer as N, OfferStepProps as O, PauseOffer as P, PlanOption as Q, ReasonButtonProps as R, SuccessStepProps as S, ReasonConfig as T, RebateOffer as U, RedirectOffer as V, ResolvedStep as W, Step as X, StructuralClassNames as Y, SubscriptionStatus as Z, SuccessClassNames as _, ConfirmStepProps as a, SurveyClassNames as a0, SurveyStep as a1, TrialExtensionOffer as a2, SurveyStepProps as b, CloseButtonProps as c, Appearance as d, AppearanceVariables as e, BuiltInOfferConfig as f, BuiltInStep as g, BuiltInStepType as h, ComponentOverrides as i, ConfirmClassNames as j, ConfirmStep as k, ContactOffer as l, CustomComponents as m, CustomOfferConfig as n, CustomOfferProps as o, CustomStepConfig as p, CustomStepProps as q, DirectCoupon as r, DirectCustomer as s, DirectPrice as t, DirectSubscription as u, DirectSubscriptionItem as v, DiscountOffer as w, FeedbackClassNames as x, FeedbackStep as y, FlowConfig as z };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@churnkey/react",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Production-ready cancel flow for React. Drop-in component, headless hook, or full customization. Works standalone or with Churnkey for AI-powered retention.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|