@churnkey/react 0.2.0 → 0.4.0
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-YSNMTOAW.cjs → chunk-4BMB42WI.cjs} +9 -7
- package/dist/chunk-4BMB42WI.cjs.map +1 -0
- package/dist/{chunk-EHZJAQQ5.cjs → chunk-BDBZ5OS3.cjs} +21 -2
- package/dist/chunk-BDBZ5OS3.cjs.map +1 -0
- package/dist/{chunk-CPCWJTSG.js → chunk-F4ESJBOS.js} +6 -4
- package/dist/chunk-F4ESJBOS.js.map +1 -0
- package/dist/{chunk-SXGORJOX.js → chunk-G57YLYM5.js} +20 -3
- package/dist/chunk-G57YLYM5.js.map +1 -0
- package/dist/{chunk-OL5HQUE4.js → chunk-QYNW3HF3.js} +66 -26
- package/dist/chunk-QYNW3HF3.js.map +1 -0
- package/dist/{chunk-ZEYUKGAR.cjs → chunk-SNTKIKHU.cjs} +66 -26
- package/dist/chunk-SNTKIKHU.cjs.map +1 -0
- package/dist/core.cjs +20 -12
- package/dist/core.d.cts +30 -5
- package/dist/core.d.ts +30 -5
- package/dist/core.js +2 -2
- package/dist/headless.cjs +3 -3
- package/dist/headless.d.cts +5 -1
- package/dist/headless.d.ts +5 -1
- package/dist/headless.js +2 -2
- package/dist/index.cjs +218 -135
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -8
- package/dist/index.d.ts +10 -8
- package/dist/index.js +125 -51
- package/dist/index.js.map +1 -1
- package/dist/{step-graph-Cbi0DRHX.d.cts → step-graph-DKvaV3Dg.d.cts} +56 -21
- package/dist/{step-graph-Cbi0DRHX.d.ts → step-graph-DKvaV3Dg.d.ts} +56 -21
- package/dist/styles.css +95 -19
- package/package.json +10 -10
- package/dist/chunk-CPCWJTSG.js.map +0 -1
- package/dist/chunk-EHZJAQQ5.cjs.map +0 -1
- package/dist/chunk-OL5HQUE4.js.map +0 -1
- package/dist/chunk-SXGORJOX.js.map +0 -1
- package/dist/chunk-YSNMTOAW.cjs.map +0 -1
- package/dist/chunk-ZEYUKGAR.cjs.map +0 -1
- package/dist/styles.css.d.ts +0 -1
|
@@ -129,12 +129,10 @@ interface PauseOffer {
|
|
|
129
129
|
type: 'pause';
|
|
130
130
|
months: number;
|
|
131
131
|
interval?: 'month' | 'week';
|
|
132
|
-
datePicker?: boolean;
|
|
133
132
|
}
|
|
134
133
|
interface PlanChangeOffer {
|
|
135
134
|
type: 'plan_change';
|
|
136
135
|
plans: PlanOption[];
|
|
137
|
-
currentPlanId?: string;
|
|
138
136
|
}
|
|
139
137
|
/**
|
|
140
138
|
* Plan option in a `plan_change` offer — `DirectPrice` plus optional
|
|
@@ -164,11 +162,23 @@ interface RedirectOffer {
|
|
|
164
162
|
url: string;
|
|
165
163
|
label: string;
|
|
166
164
|
}
|
|
165
|
+
interface RebateOffer {
|
|
166
|
+
type: 'rebate';
|
|
167
|
+
/** Cash refunded to the card, smallest currency unit. */
|
|
168
|
+
amountMinor: number;
|
|
169
|
+
currency: string;
|
|
170
|
+
/** Gross paid on the target invoice. Server-resolved in token mode. */
|
|
171
|
+
amountPaidMinor?: number;
|
|
172
|
+
/** amountPaidMinor − amountMinor. Server-resolved in token mode. */
|
|
173
|
+
netAfterRebateMinor?: number;
|
|
174
|
+
paymentMethodBrand?: string;
|
|
175
|
+
paymentMethodLast4?: string;
|
|
176
|
+
}
|
|
167
177
|
interface CustomOfferConfig {
|
|
168
178
|
type: string;
|
|
169
179
|
data?: Record<string, unknown>;
|
|
170
180
|
}
|
|
171
|
-
type BuiltInOfferConfig = DiscountOffer | PauseOffer | PlanChangeOffer | TrialExtensionOffer | ContactOffer | RedirectOffer;
|
|
181
|
+
type BuiltInOfferConfig = DiscountOffer | PauseOffer | PlanChangeOffer | TrialExtensionOffer | ContactOffer | RedirectOffer | RebateOffer;
|
|
172
182
|
type OfferConfig = BuiltInOfferConfig | CustomOfferConfig;
|
|
173
183
|
type OfferDecision = OfferConfig & {
|
|
174
184
|
copy: OfferCopy;
|
|
@@ -181,13 +191,22 @@ interface OfferCopy {
|
|
|
181
191
|
declineCta: string;
|
|
182
192
|
}
|
|
183
193
|
type AcceptedOffer = OfferConfig & {
|
|
184
|
-
|
|
185
|
-
|
|
194
|
+
/** Survey reason that routed to this offer. Absent when the offer was
|
|
195
|
+
* declared as a standalone `OfferStep`. */
|
|
196
|
+
reasonId?: string;
|
|
197
|
+
/** Payload from custom offers — whatever your component passed to
|
|
198
|
+
* `onAccept(result)`. Built-in offer types do not populate this. */
|
|
186
199
|
result?: Record<string, unknown>;
|
|
187
200
|
};
|
|
188
201
|
interface ReasonConfig {
|
|
189
202
|
id: string;
|
|
190
203
|
label: string;
|
|
204
|
+
/**
|
|
205
|
+
* When true, picking this reason reveals a text input below the reason list.
|
|
206
|
+
* The typed text lands on the session as `followupResponse`. `surveyChoiceId`
|
|
207
|
+
* still carries the reason's `id` and `surveyChoiceValue` still carries the
|
|
208
|
+
* static `label`, so analytics groupings stay stable.
|
|
209
|
+
*/
|
|
191
210
|
freeform?: boolean;
|
|
192
211
|
offer?: OfferConfig;
|
|
193
212
|
}
|
|
@@ -205,11 +224,12 @@ interface OfferStep {
|
|
|
205
224
|
title?: string;
|
|
206
225
|
description?: string;
|
|
207
226
|
/**
|
|
208
|
-
* Offer attached to this step. Set this
|
|
209
|
-
*
|
|
210
|
-
*
|
|
227
|
+
* Offer attached to this step. Set this to declare a standalone offer
|
|
228
|
+
* step (one that isn't routed from a survey reason). `copy` is optional —
|
|
229
|
+
* the SDK synthesizes default copy from the offer config when none is
|
|
230
|
+
* provided, the same way it does for survey-attached offers.
|
|
211
231
|
*/
|
|
212
|
-
offer?: OfferDecision;
|
|
232
|
+
offer?: OfferConfig | OfferDecision;
|
|
213
233
|
classNames?: OfferClassNames;
|
|
214
234
|
}
|
|
215
235
|
interface FeedbackStep {
|
|
@@ -262,7 +282,7 @@ interface SurveyClassNames {
|
|
|
262
282
|
reasonButton?: string;
|
|
263
283
|
reasonButtonSelected?: string;
|
|
264
284
|
reasonLabel?: string;
|
|
265
|
-
|
|
285
|
+
followupInput?: string;
|
|
266
286
|
continueButton?: string;
|
|
267
287
|
}
|
|
268
288
|
interface OfferClassNames {
|
|
@@ -358,9 +378,9 @@ interface AppearanceVariables {
|
|
|
358
378
|
shadowModal: string;
|
|
359
379
|
shadowCard: string;
|
|
360
380
|
/**
|
|
361
|
-
* Color of the dim behind the modal.
|
|
362
|
-
*
|
|
363
|
-
*
|
|
381
|
+
* Color of the dim behind the modal. Defaults to a neutral translucent
|
|
382
|
+
* ink. Set to `color-mix(in srgb, var(--ck-color-primary) 40%, transparent)`
|
|
383
|
+
* to derive the overlay from your primary color, or any CSS color value.
|
|
364
384
|
*/
|
|
365
385
|
overlayColor: string;
|
|
366
386
|
}
|
|
@@ -375,12 +395,14 @@ interface Appearance {
|
|
|
375
395
|
interface CustomStepProps {
|
|
376
396
|
step: CustomStepConfig;
|
|
377
397
|
customer: DirectCustomer | null;
|
|
398
|
+
subscriptions: DirectSubscription[];
|
|
378
399
|
onNext: (result?: Record<string, unknown>) => void;
|
|
379
400
|
onBack: () => void;
|
|
380
401
|
}
|
|
381
402
|
interface CustomOfferProps {
|
|
382
403
|
offer: OfferDecision;
|
|
383
404
|
customer: DirectCustomer | null;
|
|
405
|
+
subscriptions: DirectSubscription[];
|
|
384
406
|
onAccept: (result?: Record<string, unknown>) => Promise<void>;
|
|
385
407
|
onDecline: () => void;
|
|
386
408
|
isProcessing: boolean;
|
|
@@ -401,6 +423,7 @@ interface ComponentOverrides {
|
|
|
401
423
|
TrialExtensionOffer?: (props: OfferStepProps) => ReactElement;
|
|
402
424
|
ContactOffer?: (props: OfferStepProps) => ReactElement;
|
|
403
425
|
RedirectOffer?: (props: OfferStepProps) => ReactElement;
|
|
426
|
+
RebateOffer?: (props: OfferStepProps) => ReactElement;
|
|
404
427
|
}
|
|
405
428
|
type CustomComponents = Record<string, ComponentType<CustomStepProps> | ComponentType<CustomOfferProps>>;
|
|
406
429
|
interface ModalProps {
|
|
@@ -408,6 +431,7 @@ interface ModalProps {
|
|
|
408
431
|
onClose: () => void;
|
|
409
432
|
children: ReactNode;
|
|
410
433
|
className?: string;
|
|
434
|
+
overlayClassName?: string;
|
|
411
435
|
}
|
|
412
436
|
interface CloseButtonProps {
|
|
413
437
|
onClose: () => void;
|
|
@@ -420,9 +444,15 @@ interface BackButtonProps {
|
|
|
420
444
|
interface SurveyStepProps {
|
|
421
445
|
title: string;
|
|
422
446
|
description?: string;
|
|
447
|
+
customer: DirectCustomer | null;
|
|
448
|
+
subscriptions: DirectSubscription[];
|
|
423
449
|
reasons: ReasonConfig[];
|
|
424
450
|
selectedReason: string | null;
|
|
425
451
|
onSelectReason: (id: string) => void;
|
|
452
|
+
/** Free-text value when the selected reason has `freeform: true`. Lands on the session as `followupResponse`. */
|
|
453
|
+
followupResponse: string;
|
|
454
|
+
/** Set the follow-up response. The SDK forwards the value to the session. */
|
|
455
|
+
onFollowupResponseChange: (text: string) => void;
|
|
426
456
|
onNext: () => void;
|
|
427
457
|
classNames?: SurveyClassNames;
|
|
428
458
|
components?: Partial<ComponentOverrides>;
|
|
@@ -430,6 +460,8 @@ interface SurveyStepProps {
|
|
|
430
460
|
interface OfferStepProps {
|
|
431
461
|
title?: string;
|
|
432
462
|
description?: string;
|
|
463
|
+
customer: DirectCustomer | null;
|
|
464
|
+
subscriptions: DirectSubscription[];
|
|
433
465
|
offer: OfferDecision;
|
|
434
466
|
/**
|
|
435
467
|
* Accept the offer. The optional `result` is included on the resulting
|
|
@@ -450,6 +482,8 @@ interface OfferStepProps {
|
|
|
450
482
|
interface FeedbackStepProps {
|
|
451
483
|
title: string;
|
|
452
484
|
description?: string;
|
|
485
|
+
customer: DirectCustomer | null;
|
|
486
|
+
subscriptions: DirectSubscription[];
|
|
453
487
|
placeholder?: string;
|
|
454
488
|
required: boolean;
|
|
455
489
|
minLength: number;
|
|
@@ -461,11 +495,12 @@ interface FeedbackStepProps {
|
|
|
461
495
|
interface ConfirmStepProps {
|
|
462
496
|
title: string;
|
|
463
497
|
description?: string;
|
|
498
|
+
customer: DirectCustomer | null;
|
|
499
|
+
subscriptions: DirectSubscription[];
|
|
464
500
|
losses?: string[];
|
|
465
501
|
lossesLabel?: string;
|
|
466
502
|
confirmLabel: string;
|
|
467
503
|
goBackLabel: string;
|
|
468
|
-
periodEnd?: string;
|
|
469
504
|
onConfirm: () => Promise<void>;
|
|
470
505
|
onGoBack: () => void;
|
|
471
506
|
isProcessing: boolean;
|
|
@@ -476,6 +511,8 @@ interface SuccessStepProps {
|
|
|
476
511
|
offer?: OfferDecision;
|
|
477
512
|
title: string;
|
|
478
513
|
description?: string;
|
|
514
|
+
customer: DirectCustomer | null;
|
|
515
|
+
subscriptions: DirectSubscription[];
|
|
479
516
|
onClose: () => void;
|
|
480
517
|
classNames?: SuccessClassNames;
|
|
481
518
|
}
|
|
@@ -489,11 +526,13 @@ interface FlowState {
|
|
|
489
526
|
step: string;
|
|
490
527
|
currentStepId: string;
|
|
491
528
|
selectedReason: string | null;
|
|
529
|
+
followupResponse: string;
|
|
492
530
|
feedback: string;
|
|
493
531
|
outcome: 'saved' | 'cancelled' | null;
|
|
494
532
|
isProcessing: boolean;
|
|
495
533
|
error: Error | null;
|
|
496
534
|
customer: DirectCustomer | null;
|
|
535
|
+
subscriptions: DirectSubscription[];
|
|
497
536
|
}
|
|
498
537
|
interface FlowConfig extends FlowCallbacks {
|
|
499
538
|
appId?: string;
|
|
@@ -529,12 +568,14 @@ interface FlowCallbacks {
|
|
|
529
568
|
handlePause?: OfferCallback;
|
|
530
569
|
handlePlanChange?: OfferCallback;
|
|
531
570
|
handleTrialExtension?: OfferCallback;
|
|
571
|
+
handleRebate?: OfferCallback;
|
|
532
572
|
handleCancel?: CancelCallback;
|
|
533
573
|
onAccept?: OfferCallback;
|
|
534
574
|
onDiscount?: OfferCallback;
|
|
535
575
|
onPause?: OfferCallback;
|
|
536
576
|
onPlanChange?: OfferCallback;
|
|
537
577
|
onTrialExtension?: OfferCallback;
|
|
578
|
+
onRebate?: OfferCallback;
|
|
538
579
|
onCancel?: CancelCallback;
|
|
539
580
|
onClose?: () => void;
|
|
540
581
|
onStepChange?: (step: string, prevStep: string) => void;
|
|
@@ -552,12 +593,6 @@ interface CancelFlowProps extends FlowCallbacks {
|
|
|
552
593
|
classNames?: StructuralClassNames;
|
|
553
594
|
components?: Partial<ComponentOverrides>;
|
|
554
595
|
customComponents?: CustomComponents;
|
|
555
|
-
layout?: {
|
|
556
|
-
desktop?: 'modal' | 'inline' | 'drawer';
|
|
557
|
-
mobile?: 'sheet' | 'fullscreen' | 'inline';
|
|
558
|
-
breakpoint?: number;
|
|
559
|
-
};
|
|
560
|
-
animation?: 'css' | 'framer' | 'none';
|
|
561
596
|
}
|
|
562
597
|
|
|
563
598
|
interface ResolvedStep {
|
|
@@ -586,4 +621,4 @@ interface ResolvedStep {
|
|
|
586
621
|
classNames?: unknown;
|
|
587
622
|
}
|
|
588
623
|
|
|
589
|
-
export type {
|
|
624
|
+
export type { SurveyClassNames as $, AcceptedOffer as A, BackButtonProps as B, CancelFlowProps as C, DirectAddress as D, FlowState as E, FeedbackStepProps as F, OfferClassNames as G, OfferConfig as H, OfferCopy as I, OfferDecision as J, OfferStep as K, PlanChangeOffer as L, ModalProps as M, PlanOption as N, OfferStepProps as O, PauseOffer as P, ReasonConfig as Q, ReasonButtonProps as R, SuccessStepProps as S, RebateOffer as T, RedirectOffer as U, ResolvedStep as V, Step as W, StructuralClassNames as X, SubscriptionStatus as Y, SuccessClassNames as Z, SuccessStep as _, ConfirmStepProps as a, SurveyStep as a0, TrialExtensionOffer as a1, 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 };
|
|
@@ -129,12 +129,10 @@ interface PauseOffer {
|
|
|
129
129
|
type: 'pause';
|
|
130
130
|
months: number;
|
|
131
131
|
interval?: 'month' | 'week';
|
|
132
|
-
datePicker?: boolean;
|
|
133
132
|
}
|
|
134
133
|
interface PlanChangeOffer {
|
|
135
134
|
type: 'plan_change';
|
|
136
135
|
plans: PlanOption[];
|
|
137
|
-
currentPlanId?: string;
|
|
138
136
|
}
|
|
139
137
|
/**
|
|
140
138
|
* Plan option in a `plan_change` offer — `DirectPrice` plus optional
|
|
@@ -164,11 +162,23 @@ interface RedirectOffer {
|
|
|
164
162
|
url: string;
|
|
165
163
|
label: string;
|
|
166
164
|
}
|
|
165
|
+
interface RebateOffer {
|
|
166
|
+
type: 'rebate';
|
|
167
|
+
/** Cash refunded to the card, smallest currency unit. */
|
|
168
|
+
amountMinor: number;
|
|
169
|
+
currency: string;
|
|
170
|
+
/** Gross paid on the target invoice. Server-resolved in token mode. */
|
|
171
|
+
amountPaidMinor?: number;
|
|
172
|
+
/** amountPaidMinor − amountMinor. Server-resolved in token mode. */
|
|
173
|
+
netAfterRebateMinor?: number;
|
|
174
|
+
paymentMethodBrand?: string;
|
|
175
|
+
paymentMethodLast4?: string;
|
|
176
|
+
}
|
|
167
177
|
interface CustomOfferConfig {
|
|
168
178
|
type: string;
|
|
169
179
|
data?: Record<string, unknown>;
|
|
170
180
|
}
|
|
171
|
-
type BuiltInOfferConfig = DiscountOffer | PauseOffer | PlanChangeOffer | TrialExtensionOffer | ContactOffer | RedirectOffer;
|
|
181
|
+
type BuiltInOfferConfig = DiscountOffer | PauseOffer | PlanChangeOffer | TrialExtensionOffer | ContactOffer | RedirectOffer | RebateOffer;
|
|
172
182
|
type OfferConfig = BuiltInOfferConfig | CustomOfferConfig;
|
|
173
183
|
type OfferDecision = OfferConfig & {
|
|
174
184
|
copy: OfferCopy;
|
|
@@ -181,13 +191,22 @@ interface OfferCopy {
|
|
|
181
191
|
declineCta: string;
|
|
182
192
|
}
|
|
183
193
|
type AcceptedOffer = OfferConfig & {
|
|
184
|
-
|
|
185
|
-
|
|
194
|
+
/** Survey reason that routed to this offer. Absent when the offer was
|
|
195
|
+
* declared as a standalone `OfferStep`. */
|
|
196
|
+
reasonId?: string;
|
|
197
|
+
/** Payload from custom offers — whatever your component passed to
|
|
198
|
+
* `onAccept(result)`. Built-in offer types do not populate this. */
|
|
186
199
|
result?: Record<string, unknown>;
|
|
187
200
|
};
|
|
188
201
|
interface ReasonConfig {
|
|
189
202
|
id: string;
|
|
190
203
|
label: string;
|
|
204
|
+
/**
|
|
205
|
+
* When true, picking this reason reveals a text input below the reason list.
|
|
206
|
+
* The typed text lands on the session as `followupResponse`. `surveyChoiceId`
|
|
207
|
+
* still carries the reason's `id` and `surveyChoiceValue` still carries the
|
|
208
|
+
* static `label`, so analytics groupings stay stable.
|
|
209
|
+
*/
|
|
191
210
|
freeform?: boolean;
|
|
192
211
|
offer?: OfferConfig;
|
|
193
212
|
}
|
|
@@ -205,11 +224,12 @@ interface OfferStep {
|
|
|
205
224
|
title?: string;
|
|
206
225
|
description?: string;
|
|
207
226
|
/**
|
|
208
|
-
* Offer attached to this step. Set this
|
|
209
|
-
*
|
|
210
|
-
*
|
|
227
|
+
* Offer attached to this step. Set this to declare a standalone offer
|
|
228
|
+
* step (one that isn't routed from a survey reason). `copy` is optional —
|
|
229
|
+
* the SDK synthesizes default copy from the offer config when none is
|
|
230
|
+
* provided, the same way it does for survey-attached offers.
|
|
211
231
|
*/
|
|
212
|
-
offer?: OfferDecision;
|
|
232
|
+
offer?: OfferConfig | OfferDecision;
|
|
213
233
|
classNames?: OfferClassNames;
|
|
214
234
|
}
|
|
215
235
|
interface FeedbackStep {
|
|
@@ -262,7 +282,7 @@ interface SurveyClassNames {
|
|
|
262
282
|
reasonButton?: string;
|
|
263
283
|
reasonButtonSelected?: string;
|
|
264
284
|
reasonLabel?: string;
|
|
265
|
-
|
|
285
|
+
followupInput?: string;
|
|
266
286
|
continueButton?: string;
|
|
267
287
|
}
|
|
268
288
|
interface OfferClassNames {
|
|
@@ -358,9 +378,9 @@ interface AppearanceVariables {
|
|
|
358
378
|
shadowModal: string;
|
|
359
379
|
shadowCard: string;
|
|
360
380
|
/**
|
|
361
|
-
* Color of the dim behind the modal.
|
|
362
|
-
*
|
|
363
|
-
*
|
|
381
|
+
* Color of the dim behind the modal. Defaults to a neutral translucent
|
|
382
|
+
* ink. Set to `color-mix(in srgb, var(--ck-color-primary) 40%, transparent)`
|
|
383
|
+
* to derive the overlay from your primary color, or any CSS color value.
|
|
364
384
|
*/
|
|
365
385
|
overlayColor: string;
|
|
366
386
|
}
|
|
@@ -375,12 +395,14 @@ interface Appearance {
|
|
|
375
395
|
interface CustomStepProps {
|
|
376
396
|
step: CustomStepConfig;
|
|
377
397
|
customer: DirectCustomer | null;
|
|
398
|
+
subscriptions: DirectSubscription[];
|
|
378
399
|
onNext: (result?: Record<string, unknown>) => void;
|
|
379
400
|
onBack: () => void;
|
|
380
401
|
}
|
|
381
402
|
interface CustomOfferProps {
|
|
382
403
|
offer: OfferDecision;
|
|
383
404
|
customer: DirectCustomer | null;
|
|
405
|
+
subscriptions: DirectSubscription[];
|
|
384
406
|
onAccept: (result?: Record<string, unknown>) => Promise<void>;
|
|
385
407
|
onDecline: () => void;
|
|
386
408
|
isProcessing: boolean;
|
|
@@ -401,6 +423,7 @@ interface ComponentOverrides {
|
|
|
401
423
|
TrialExtensionOffer?: (props: OfferStepProps) => ReactElement;
|
|
402
424
|
ContactOffer?: (props: OfferStepProps) => ReactElement;
|
|
403
425
|
RedirectOffer?: (props: OfferStepProps) => ReactElement;
|
|
426
|
+
RebateOffer?: (props: OfferStepProps) => ReactElement;
|
|
404
427
|
}
|
|
405
428
|
type CustomComponents = Record<string, ComponentType<CustomStepProps> | ComponentType<CustomOfferProps>>;
|
|
406
429
|
interface ModalProps {
|
|
@@ -408,6 +431,7 @@ interface ModalProps {
|
|
|
408
431
|
onClose: () => void;
|
|
409
432
|
children: ReactNode;
|
|
410
433
|
className?: string;
|
|
434
|
+
overlayClassName?: string;
|
|
411
435
|
}
|
|
412
436
|
interface CloseButtonProps {
|
|
413
437
|
onClose: () => void;
|
|
@@ -420,9 +444,15 @@ interface BackButtonProps {
|
|
|
420
444
|
interface SurveyStepProps {
|
|
421
445
|
title: string;
|
|
422
446
|
description?: string;
|
|
447
|
+
customer: DirectCustomer | null;
|
|
448
|
+
subscriptions: DirectSubscription[];
|
|
423
449
|
reasons: ReasonConfig[];
|
|
424
450
|
selectedReason: string | null;
|
|
425
451
|
onSelectReason: (id: string) => void;
|
|
452
|
+
/** Free-text value when the selected reason has `freeform: true`. Lands on the session as `followupResponse`. */
|
|
453
|
+
followupResponse: string;
|
|
454
|
+
/** Set the follow-up response. The SDK forwards the value to the session. */
|
|
455
|
+
onFollowupResponseChange: (text: string) => void;
|
|
426
456
|
onNext: () => void;
|
|
427
457
|
classNames?: SurveyClassNames;
|
|
428
458
|
components?: Partial<ComponentOverrides>;
|
|
@@ -430,6 +460,8 @@ interface SurveyStepProps {
|
|
|
430
460
|
interface OfferStepProps {
|
|
431
461
|
title?: string;
|
|
432
462
|
description?: string;
|
|
463
|
+
customer: DirectCustomer | null;
|
|
464
|
+
subscriptions: DirectSubscription[];
|
|
433
465
|
offer: OfferDecision;
|
|
434
466
|
/**
|
|
435
467
|
* Accept the offer. The optional `result` is included on the resulting
|
|
@@ -450,6 +482,8 @@ interface OfferStepProps {
|
|
|
450
482
|
interface FeedbackStepProps {
|
|
451
483
|
title: string;
|
|
452
484
|
description?: string;
|
|
485
|
+
customer: DirectCustomer | null;
|
|
486
|
+
subscriptions: DirectSubscription[];
|
|
453
487
|
placeholder?: string;
|
|
454
488
|
required: boolean;
|
|
455
489
|
minLength: number;
|
|
@@ -461,11 +495,12 @@ interface FeedbackStepProps {
|
|
|
461
495
|
interface ConfirmStepProps {
|
|
462
496
|
title: string;
|
|
463
497
|
description?: string;
|
|
498
|
+
customer: DirectCustomer | null;
|
|
499
|
+
subscriptions: DirectSubscription[];
|
|
464
500
|
losses?: string[];
|
|
465
501
|
lossesLabel?: string;
|
|
466
502
|
confirmLabel: string;
|
|
467
503
|
goBackLabel: string;
|
|
468
|
-
periodEnd?: string;
|
|
469
504
|
onConfirm: () => Promise<void>;
|
|
470
505
|
onGoBack: () => void;
|
|
471
506
|
isProcessing: boolean;
|
|
@@ -476,6 +511,8 @@ interface SuccessStepProps {
|
|
|
476
511
|
offer?: OfferDecision;
|
|
477
512
|
title: string;
|
|
478
513
|
description?: string;
|
|
514
|
+
customer: DirectCustomer | null;
|
|
515
|
+
subscriptions: DirectSubscription[];
|
|
479
516
|
onClose: () => void;
|
|
480
517
|
classNames?: SuccessClassNames;
|
|
481
518
|
}
|
|
@@ -489,11 +526,13 @@ interface FlowState {
|
|
|
489
526
|
step: string;
|
|
490
527
|
currentStepId: string;
|
|
491
528
|
selectedReason: string | null;
|
|
529
|
+
followupResponse: string;
|
|
492
530
|
feedback: string;
|
|
493
531
|
outcome: 'saved' | 'cancelled' | null;
|
|
494
532
|
isProcessing: boolean;
|
|
495
533
|
error: Error | null;
|
|
496
534
|
customer: DirectCustomer | null;
|
|
535
|
+
subscriptions: DirectSubscription[];
|
|
497
536
|
}
|
|
498
537
|
interface FlowConfig extends FlowCallbacks {
|
|
499
538
|
appId?: string;
|
|
@@ -529,12 +568,14 @@ interface FlowCallbacks {
|
|
|
529
568
|
handlePause?: OfferCallback;
|
|
530
569
|
handlePlanChange?: OfferCallback;
|
|
531
570
|
handleTrialExtension?: OfferCallback;
|
|
571
|
+
handleRebate?: OfferCallback;
|
|
532
572
|
handleCancel?: CancelCallback;
|
|
533
573
|
onAccept?: OfferCallback;
|
|
534
574
|
onDiscount?: OfferCallback;
|
|
535
575
|
onPause?: OfferCallback;
|
|
536
576
|
onPlanChange?: OfferCallback;
|
|
537
577
|
onTrialExtension?: OfferCallback;
|
|
578
|
+
onRebate?: OfferCallback;
|
|
538
579
|
onCancel?: CancelCallback;
|
|
539
580
|
onClose?: () => void;
|
|
540
581
|
onStepChange?: (step: string, prevStep: string) => void;
|
|
@@ -552,12 +593,6 @@ interface CancelFlowProps extends FlowCallbacks {
|
|
|
552
593
|
classNames?: StructuralClassNames;
|
|
553
594
|
components?: Partial<ComponentOverrides>;
|
|
554
595
|
customComponents?: CustomComponents;
|
|
555
|
-
layout?: {
|
|
556
|
-
desktop?: 'modal' | 'inline' | 'drawer';
|
|
557
|
-
mobile?: 'sheet' | 'fullscreen' | 'inline';
|
|
558
|
-
breakpoint?: number;
|
|
559
|
-
};
|
|
560
|
-
animation?: 'css' | 'framer' | 'none';
|
|
561
596
|
}
|
|
562
597
|
|
|
563
598
|
interface ResolvedStep {
|
|
@@ -586,4 +621,4 @@ interface ResolvedStep {
|
|
|
586
621
|
classNames?: unknown;
|
|
587
622
|
}
|
|
588
623
|
|
|
589
|
-
export type {
|
|
624
|
+
export type { SurveyClassNames as $, AcceptedOffer as A, BackButtonProps as B, CancelFlowProps as C, DirectAddress as D, FlowState as E, FeedbackStepProps as F, OfferClassNames as G, OfferConfig as H, OfferCopy as I, OfferDecision as J, OfferStep as K, PlanChangeOffer as L, ModalProps as M, PlanOption as N, OfferStepProps as O, PauseOffer as P, ReasonConfig as Q, ReasonButtonProps as R, SuccessStepProps as S, RebateOffer as T, RedirectOffer as U, ResolvedStep as V, Step as W, StructuralClassNames as X, SubscriptionStatus as Y, SuccessClassNames as Z, SuccessStep as _, ConfirmStepProps as a, SurveyStep as a0, TrialExtensionOffer as a1, 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 };
|
package/dist/styles.css
CHANGED
|
@@ -71,12 +71,11 @@
|
|
|
71
71
|
--ck-shadow-modal: 0 20px 25px -5px rgba(12, 10, 9, 0.1), 0 8px 10px -6px rgba(12, 10, 9, 0.06);
|
|
72
72
|
--ck-shadow-card: 0 1px 2px rgba(12, 10, 9, 0.04), 0 1px 3px rgba(12, 10, 9, 0.06);
|
|
73
73
|
|
|
74
|
-
/*
|
|
75
|
-
primary
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
--ck-overlay-color: color-mix(in srgb, var(--ck-color-primary) 40%, transparent);
|
|
74
|
+
/* Dim behind the modal. Neutral translucent ink by default. Brands that
|
|
75
|
+
want a primary-tinted overlay can override this with their own value —
|
|
76
|
+
e.g. `color-mix(in srgb, var(--ck-color-primary) 40%, transparent)` for
|
|
77
|
+
auto re-tinting on primary swaps. */
|
|
78
|
+
--ck-overlay-color: rgba(12, 10, 9, 0.5);
|
|
80
79
|
|
|
81
80
|
/* Motion */
|
|
82
81
|
--ck-motion-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
@@ -144,11 +143,6 @@
|
|
|
144
143
|
display: flex;
|
|
145
144
|
align-items: center;
|
|
146
145
|
justify-content: center;
|
|
147
|
-
/* Fallback for browsers without color-mix() support — used if the token
|
|
148
|
-
evaluates to an invalid value. Modern engines (Chrome 111+, Safari
|
|
149
|
-
16.2+, Firefox 113+) resolve --ck-overlay-color from the :root rule. */
|
|
150
|
-
background: rgba(12, 10, 9, 0.5);
|
|
151
|
-
/* biome-ignore lint/suspicious/noDuplicateProperties: progressive enhancement fallback */
|
|
152
146
|
background: var(--ck-overlay-color);
|
|
153
147
|
z-index: 9999;
|
|
154
148
|
}
|
|
@@ -353,6 +347,25 @@
|
|
|
353
347
|
margin-bottom: 20px;
|
|
354
348
|
}
|
|
355
349
|
|
|
350
|
+
.ck-cancel-flow .ck-reason-followup {
|
|
351
|
+
width: 100%;
|
|
352
|
+
font-family: var(--ck-font);
|
|
353
|
+
font-size: 14px;
|
|
354
|
+
color: var(--ck-color-text);
|
|
355
|
+
background: var(--ck-color-surface);
|
|
356
|
+
border: 1px solid var(--ck-color-border);
|
|
357
|
+
border-radius: var(--ck-radius-md);
|
|
358
|
+
padding: 10px 12px;
|
|
359
|
+
margin-bottom: 20px;
|
|
360
|
+
resize: vertical;
|
|
361
|
+
min-height: 72px;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.ck-cancel-flow .ck-reason-followup:focus {
|
|
365
|
+
outline: none;
|
|
366
|
+
border-color: var(--ck-color-primary);
|
|
367
|
+
}
|
|
368
|
+
|
|
356
369
|
.ck-cancel-flow .ck-reason-button {
|
|
357
370
|
display: flex;
|
|
358
371
|
align-items: center;
|
|
@@ -418,10 +431,17 @@
|
|
|
418
431
|
margin-bottom: 20px;
|
|
419
432
|
}
|
|
420
433
|
|
|
421
|
-
/*
|
|
422
|
-
.
|
|
423
|
-
|
|
434
|
+
/* Offer panels are soft callouts (colorSurfaceMuted), not selected-state
|
|
435
|
+
highlights (colorPrimarySoft). Shared so the offer types stay consistent. */
|
|
436
|
+
.ck-cancel-flow .ck-offer-discount,
|
|
437
|
+
.ck-cancel-flow .ck-pause-card,
|
|
438
|
+
.ck-cancel-flow .ck-trial-block {
|
|
439
|
+
background: var(--ck-color-surface-muted);
|
|
424
440
|
border-radius: var(--ck-radius-lg);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/* Discount: centered phrase. */
|
|
444
|
+
.ck-cancel-flow .ck-offer-discount {
|
|
425
445
|
padding: 24px 20px;
|
|
426
446
|
text-align: center;
|
|
427
447
|
}
|
|
@@ -444,11 +464,43 @@
|
|
|
444
464
|
color: var(--ck-color-text);
|
|
445
465
|
}
|
|
446
466
|
|
|
467
|
+
/* Rebate: an itemized invoice for the period — the charge, the rebate we
|
|
468
|
+
credit (accented), and what's still due. Lines sit on the surface rather
|
|
469
|
+
than in a filled panel. */
|
|
470
|
+
.ck-cancel-flow .ck-offer-rebate {
|
|
471
|
+
margin: 4px 0 26px;
|
|
472
|
+
font-variant-numeric: tabular-nums;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.ck-cancel-flow .ck-offer-rebate-row {
|
|
476
|
+
display: flex;
|
|
477
|
+
justify-content: space-between;
|
|
478
|
+
align-items: baseline;
|
|
479
|
+
gap: 12px;
|
|
480
|
+
padding: 7px 0;
|
|
481
|
+
font-size: 13px;
|
|
482
|
+
color: var(--ck-color-text-muted);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/* The rebate line — the figure we want the eye to land on. */
|
|
486
|
+
.ck-cancel-flow .ck-offer-rebate-credit {
|
|
487
|
+
font-size: 14px;
|
|
488
|
+
font-weight: 600;
|
|
489
|
+
color: var(--ck-color-primary);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.ck-cancel-flow .ck-offer-rebate-total {
|
|
493
|
+
margin-top: 6px;
|
|
494
|
+
padding-top: 14px;
|
|
495
|
+
border-top: 1px solid var(--ck-color-border);
|
|
496
|
+
font-size: 15px;
|
|
497
|
+
font-weight: 600;
|
|
498
|
+
color: var(--ck-color-text);
|
|
499
|
+
}
|
|
500
|
+
|
|
447
501
|
/* Pause: resume date is the typographic anchor, month chips below. */
|
|
448
502
|
.ck-cancel-flow .ck-pause-card {
|
|
449
503
|
padding: 32px 24px 22px;
|
|
450
|
-
background: var(--ck-color-surface-muted);
|
|
451
|
-
border-radius: var(--ck-radius-lg);
|
|
452
504
|
text-align: center;
|
|
453
505
|
margin: 8px 0 20px;
|
|
454
506
|
}
|
|
@@ -502,14 +554,12 @@
|
|
|
502
554
|
border-color: var(--ck-color-primary);
|
|
503
555
|
}
|
|
504
556
|
|
|
505
|
-
/* Trial extension: day count badge + new end date
|
|
557
|
+
/* Trial extension: day count badge + new end date. */
|
|
506
558
|
.ck-cancel-flow .ck-trial-block {
|
|
507
559
|
display: flex;
|
|
508
560
|
align-items: center;
|
|
509
561
|
gap: 16px;
|
|
510
562
|
padding: 20px;
|
|
511
|
-
background: var(--ck-color-primary-soft);
|
|
512
|
-
border-radius: var(--ck-radius-lg);
|
|
513
563
|
}
|
|
514
564
|
|
|
515
565
|
.ck-cancel-flow .ck-trial-badge {
|
|
@@ -613,6 +663,17 @@
|
|
|
613
663
|
box-shadow: var(--ck-shadow-card);
|
|
614
664
|
}
|
|
615
665
|
|
|
666
|
+
.ck-cancel-flow .ck-plan-card--current {
|
|
667
|
+
cursor: not-allowed;
|
|
668
|
+
opacity: 0.6;
|
|
669
|
+
background: var(--ck-color-surface-muted);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.ck-cancel-flow .ck-plan-card--current:hover {
|
|
673
|
+
border-color: var(--ck-color-border);
|
|
674
|
+
background: var(--ck-color-surface-muted);
|
|
675
|
+
}
|
|
676
|
+
|
|
616
677
|
.ck-cancel-flow .ck-plan-name {
|
|
617
678
|
font-family: var(--ck-font-display);
|
|
618
679
|
font-size: 16px;
|
|
@@ -620,6 +681,21 @@
|
|
|
620
681
|
letter-spacing: -0.01em;
|
|
621
682
|
color: var(--ck-color-text);
|
|
622
683
|
margin-bottom: 4px;
|
|
684
|
+
display: flex;
|
|
685
|
+
align-items: center;
|
|
686
|
+
gap: 8px;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.ck-cancel-flow .ck-plan-current-badge {
|
|
690
|
+
font-family: var(--ck-font);
|
|
691
|
+
font-size: 11px;
|
|
692
|
+
font-weight: 500;
|
|
693
|
+
letter-spacing: 0;
|
|
694
|
+
color: var(--ck-color-text-secondary);
|
|
695
|
+
background: var(--ck-color-surface);
|
|
696
|
+
border: 1px solid var(--ck-color-border);
|
|
697
|
+
padding: 2px 6px;
|
|
698
|
+
border-radius: var(--ck-radius-sm);
|
|
623
699
|
}
|
|
624
700
|
|
|
625
701
|
.ck-cancel-flow .ck-plan-tagline {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@churnkey/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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": {
|
|
@@ -65,6 +65,14 @@
|
|
|
65
65
|
"dist",
|
|
66
66
|
"package.json"
|
|
67
67
|
],
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "tsup && node -e \"require('fs').writeFileSync('dist/styles.css.d.ts','export {}\\n')\"",
|
|
70
|
+
"dev": "tsup --watch",
|
|
71
|
+
"test": "vitest run",
|
|
72
|
+
"test:watch": "vitest",
|
|
73
|
+
"typecheck": "tsc --noEmit",
|
|
74
|
+
"lint": "biome check ."
|
|
75
|
+
},
|
|
68
76
|
"peerDependencies": {
|
|
69
77
|
"react": "^18.0.0 || ^19.0.0",
|
|
70
78
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
@@ -82,13 +90,5 @@
|
|
|
82
90
|
"tsup": "^8.0.0",
|
|
83
91
|
"typescript": "^5.5.0",
|
|
84
92
|
"vitest": "^2.0.0"
|
|
85
|
-
},
|
|
86
|
-
"scripts": {
|
|
87
|
-
"build": "tsup && node -e \"require('fs').writeFileSync('dist/styles.css.d.ts','export {}\\n')\"",
|
|
88
|
-
"dev": "tsup --watch",
|
|
89
|
-
"test": "vitest run",
|
|
90
|
-
"test:watch": "vitest",
|
|
91
|
-
"typecheck": "tsc --noEmit",
|
|
92
|
-
"lint": "biome check ."
|
|
93
93
|
}
|
|
94
|
-
}
|
|
94
|
+
}
|