@funnelsgrove/payments 0.1.17 → 0.1.18
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.
|
@@ -22,6 +22,8 @@ type ManageSubscription = RuntimeSubscription & {
|
|
|
22
22
|
export declare function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId, plans, }: ManageSubscriptionScreenProps): import("react/jsx-runtime").JSX.Element;
|
|
23
23
|
export declare const __manageSubscriptionScreenTestables: {
|
|
24
24
|
resolveBillingDateLabel: (subscription: Pick<ManageSubscription, "cancelAtPeriodEnd" | "currentPeriodEnd" | "status">, options?: {
|
|
25
|
+
cancelled?: boolean;
|
|
25
26
|
past?: boolean;
|
|
26
27
|
}) => string;
|
|
28
|
+
resolveSubscriptionStatusLabel: (subscription: ManageSubscription) => string;
|
|
27
29
|
};
|
|
@@ -4,9 +4,13 @@ import { useEffect, useMemo, useState } from 'react';
|
|
|
4
4
|
import { apiService, buildPreviewManageSubscriptionsFallback, isPreviewFrameRuntime, runtimePublicConfig, useFunnel, } from '@funnelsgrove/runtime';
|
|
5
5
|
import { getFallbackBillingPlans } from '../services/planCatalog.service.js';
|
|
6
6
|
const pastSubscriptionsNote = 'Past subscriptions are already canceled or expired. Dates below are historical, not upcoming charges.';
|
|
7
|
+
const cancelledSubscriptionsNote = 'These subscriptions are cancelled and remain active until the date shown below.';
|
|
8
|
+
const getNormalizedStatus = (subscription) => subscription.status.trim().toLowerCase();
|
|
9
|
+
const isCancelledActiveSubscription = (subscription) => {
|
|
10
|
+
return subscription.cancelAtPeriodEnd && getNormalizedStatus(subscription) !== 'canceled';
|
|
11
|
+
};
|
|
7
12
|
const isCancellableSubscription = (subscription) => {
|
|
8
|
-
|
|
9
|
-
return normalizedStatus !== 'canceled' && !subscription.cancelAtPeriodEnd;
|
|
13
|
+
return getNormalizedStatus(subscription) !== 'canceled' && !subscription.cancelAtPeriodEnd;
|
|
10
14
|
};
|
|
11
15
|
const formatDate = (value) => {
|
|
12
16
|
if (!value) {
|
|
@@ -51,8 +55,8 @@ const resolveCreatedDate = (subscription) => {
|
|
|
51
55
|
return formatDate(subscription.createdAt || subscription.currentPeriodStart);
|
|
52
56
|
};
|
|
53
57
|
const resolveBillingDateLabel = (subscription, options = {}) => {
|
|
54
|
-
const normalizedStatus = subscription
|
|
55
|
-
if (subscription.cancelAtPeriodEnd) {
|
|
58
|
+
const normalizedStatus = getNormalizedStatus(subscription);
|
|
59
|
+
if (options.cancelled || subscription.cancelAtPeriodEnd) {
|
|
56
60
|
return `Active until: ${formatDate(subscription.currentPeriodEnd)}`;
|
|
57
61
|
}
|
|
58
62
|
if (options.past && normalizedStatus === 'canceled') {
|
|
@@ -64,6 +68,16 @@ const resolveBillingDateLabel = (subscription, options = {}) => {
|
|
|
64
68
|
return `Next charge: ${formatDate(subscription.currentPeriodEnd)}`;
|
|
65
69
|
};
|
|
66
70
|
const toManageSubscriptions = (subscriptions) => subscriptions;
|
|
71
|
+
const resolveSubscriptionStatusLabel = (subscription) => {
|
|
72
|
+
if (isCancelledActiveSubscription(subscription)) {
|
|
73
|
+
return 'Cancelled';
|
|
74
|
+
}
|
|
75
|
+
const normalizedStatus = getNormalizedStatus(subscription);
|
|
76
|
+
if (!normalizedStatus) {
|
|
77
|
+
return 'Active';
|
|
78
|
+
}
|
|
79
|
+
return `${normalizedStatus.charAt(0).toUpperCase()}${normalizedStatus.slice(1)}`;
|
|
80
|
+
};
|
|
67
81
|
export function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId, plans, }) {
|
|
68
82
|
var _a, _b;
|
|
69
83
|
const { attributes, completeStep, goToStep, setAnswer, setUser, user } = useFunnel();
|
|
@@ -114,7 +128,8 @@ export function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId,
|
|
|
114
128
|
}, [content.errorMessages.load]);
|
|
115
129
|
const subscriptions = useMemo(() => { var _a; return toManageSubscriptions((_a = data === null || data === void 0 ? void 0 : data.subscriptions) !== null && _a !== void 0 ? _a : []); }, [data]);
|
|
116
130
|
const activeSubscriptions = useMemo(() => subscriptions.filter(isCancellableSubscription), [subscriptions]);
|
|
117
|
-
const
|
|
131
|
+
const cancelledSubscriptions = useMemo(() => subscriptions.filter(isCancelledActiveSubscription), [subscriptions]);
|
|
132
|
+
const pastSubscriptions = useMemo(() => subscriptions.filter((subscription) => !isCancellableSubscription(subscription) && !isCancelledActiveSubscription(subscription)), [subscriptions]);
|
|
118
133
|
const selectedSubscription = (_a = activeSubscriptions.find((subscription) => subscription.id === selectedSubscriptionId)) !== null && _a !== void 0 ? _a : null;
|
|
119
134
|
const selectedReason = (_b = content.whyStage.reasons.find((reason) => reason.id === selectedReasonId)) !== null && _b !== void 0 ? _b : null;
|
|
120
135
|
const handleSubscriptionSelected = (subscription) => {
|
|
@@ -192,11 +207,11 @@ export function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId,
|
|
|
192
207
|
setCancelInFlight(false);
|
|
193
208
|
}
|
|
194
209
|
};
|
|
195
|
-
return (_jsxs(_Fragment, { children: [_jsxs("section", { className: 'manage-subscription', "data-node-id": nodeId || stepId, children: [stage === 'subscriptions' ? (_jsxs("div", { className: 'manage-subscription-inner', children: [_jsx("h1", { className: 'manage-subscription-title', children: content.subscriptionsStage.title }), loading ? (_jsx("p", { className: 'manage-subscription-muted', children: content.subscriptionsStage.loadingLabel })) : null, error ? _jsx("p", { className: 'manage-subscription-error', children: error }) : null, !loading && !error && subscriptions.length === 0 ? (_jsx("p", { className: 'manage-subscription-empty', children: content.subscriptionsStage.emptyLabel })) : null, !loading && activeSubscriptions.length > 0 ? (_jsx(SubscriptionSection, { title: 'Active Subscriptions', subscriptions: activeSubscriptions, plans: planList, onSelect: handleSubscriptionSelected })) : null, !loading && pastSubscriptions.length > 0 ? (_jsx(SubscriptionSection, { title: 'Past Subscriptions', subscriptions: pastSubscriptions, plans: planList, onSelect: handleSubscriptionSelected, description: pastSubscriptionsNote, past: true })) : null, _jsxs("p", { className: 'manage-subscription-support', children: [content.subscriptionsStage.supportPrefix, ' ', _jsx("a", { href: `mailto:${supportEmail}`, children: supportEmail })] })] })) : null, stage === 'why' ? (_jsxs("div", { className: 'manage-subscription-inner', children: [_jsx("h1", { className: 'manage-subscription-title is-reason', children: content.whyStage.title }), _jsx("ul", { className: 'manage-subscription-reasons', role: 'list', "aria-label": content.whyStage.reasonsAriaLabel, children: content.whyStage.reasons.map((reason) => (_jsx("li", { children: _jsxs("button", { type: 'button', className: 'manage-subscription-reason', onClick: () => handleReasonSelected(reason.id), children: [_jsx("span", { className: 'manage-subscription-reason-icon', "aria-hidden": true, children: reason.icon }), _jsx("span", { children: reason.label }), _jsx("span", { className: 'manage-subscription-reason-arrow', "aria-hidden": true, children: ">" })] }) }, reason.id))) })] })) : null, stage === 'confirm' ? (_jsxs("div", { className: 'manage-subscription-inner is-short', children: [_jsx("h1", { className: 'manage-subscription-title', children: content.confirmStage.title }), error ? _jsx("p", { className: 'manage-subscription-error', children: error }) : null, _jsx("button", { type: 'button', className: 'manage-subscription-primary', disabled: !selectedSubscription || !selectedReason || cancelInFlight, onClick: () => {
|
|
210
|
+
return (_jsxs(_Fragment, { children: [_jsxs("section", { className: 'manage-subscription', "data-node-id": nodeId || stepId, children: [stage === 'subscriptions' ? (_jsxs("div", { className: 'manage-subscription-inner', children: [_jsx("h1", { className: 'manage-subscription-title', children: content.subscriptionsStage.title }), loading ? (_jsx("p", { className: 'manage-subscription-muted', children: content.subscriptionsStage.loadingLabel })) : null, error ? _jsx("p", { className: 'manage-subscription-error', children: error }) : null, !loading && !error && subscriptions.length === 0 ? (_jsx("p", { className: 'manage-subscription-empty', children: content.subscriptionsStage.emptyLabel })) : null, !loading && activeSubscriptions.length > 0 ? (_jsx(SubscriptionSection, { title: 'Active Subscriptions', subscriptions: activeSubscriptions, plans: planList, onSelect: handleSubscriptionSelected })) : null, !loading && cancelledSubscriptions.length > 0 ? (_jsx(SubscriptionSection, { title: 'Cancelled Subscriptions', subscriptions: cancelledSubscriptions, plans: planList, onSelect: handleSubscriptionSelected, description: cancelledSubscriptionsNote, cancelled: true })) : null, !loading && pastSubscriptions.length > 0 ? (_jsx(SubscriptionSection, { title: 'Past Subscriptions', subscriptions: pastSubscriptions, plans: planList, onSelect: handleSubscriptionSelected, description: pastSubscriptionsNote, past: true })) : null, _jsxs("p", { className: 'manage-subscription-support', children: [content.subscriptionsStage.supportPrefix, ' ', _jsx("a", { href: `mailto:${supportEmail}`, children: supportEmail })] })] })) : null, stage === 'why' ? (_jsxs("div", { className: 'manage-subscription-inner', children: [_jsx("h1", { className: 'manage-subscription-title is-reason', children: content.whyStage.title }), _jsx("ul", { className: 'manage-subscription-reasons', role: 'list', "aria-label": content.whyStage.reasonsAriaLabel, children: content.whyStage.reasons.map((reason) => (_jsx("li", { children: _jsxs("button", { type: 'button', className: 'manage-subscription-reason', onClick: () => handleReasonSelected(reason.id), children: [_jsx("span", { className: 'manage-subscription-reason-icon', "aria-hidden": true, children: reason.icon }), _jsx("span", { children: reason.label }), _jsx("span", { className: 'manage-subscription-reason-arrow', "aria-hidden": true, children: ">" })] }) }, reason.id))) })] })) : null, stage === 'confirm' ? (_jsxs("div", { className: 'manage-subscription-inner is-short', children: [_jsx("h1", { className: 'manage-subscription-title', children: content.confirmStage.title }), error ? _jsx("p", { className: 'manage-subscription-error', children: error }) : null, _jsx("button", { type: 'button', className: 'manage-subscription-primary', disabled: !selectedSubscription || !selectedReason || cancelInFlight, onClick: () => {
|
|
196
211
|
void handleCancelSubscription();
|
|
197
212
|
}, children: cancelInFlight
|
|
198
213
|
? content.confirmStage.cancellingLabel
|
|
199
|
-
: content.confirmStage.cancelLabel })] })) : null, stage === 'done' ? (_jsxs("div", { className: 'manage-subscription-inner is-short', children: [_jsx("h1", { className: 'manage-subscription-title', children: content.doneStage.title }), _jsx("p", { className: 'manage-subscription-done-copy', children: content.doneStage.cancelledMessage }), cancelledActiveUntilDate ? (_jsxs("p", { className: 'manage-subscription-done-copy is-active-until', children: ["Active until: ", cancelledActiveUntilDate] })) : null, _jsx("button", { type: 'button', className: 'manage-subscription-primary', onClick: () => {
|
|
214
|
+
: content.confirmStage.cancelLabel })] })) : null, stage === 'done' ? (_jsxs("div", { className: 'manage-subscription-inner is-short', children: [_jsx("h1", { className: 'manage-subscription-title', children: content.doneStage.title }), _jsx("p", { className: 'manage-subscription-done-status', children: "Cancelled" }), _jsx("p", { className: 'manage-subscription-done-copy', children: content.doneStage.cancelledMessage }), cancelledActiveUntilDate ? (_jsxs("p", { className: 'manage-subscription-done-copy is-active-until', children: ["Active until: ", cancelledActiveUntilDate] })) : null, _jsx("button", { type: 'button', className: 'manage-subscription-primary', onClick: () => {
|
|
200
215
|
if (homeStepId) {
|
|
201
216
|
goToStep(homeStepId);
|
|
202
217
|
return;
|
|
@@ -204,8 +219,16 @@ export function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId,
|
|
|
204
219
|
setStage('subscriptions');
|
|
205
220
|
}, children: content.doneStage.returnHomeLabel })] })) : null] }), _jsx("style", { children: manageSubscriptionStyles })] }));
|
|
206
221
|
}
|
|
207
|
-
function SubscriptionSection({ title, subscriptions, plans, onSelect, description, past = false, }) {
|
|
208
|
-
|
|
222
|
+
function SubscriptionSection({ title, subscriptions, plans, onSelect, description, cancelled = false, past = false, }) {
|
|
223
|
+
const isDisabled = cancelled || past;
|
|
224
|
+
const rowClassName = cancelled
|
|
225
|
+
? 'manage-subscription-row is-cancelled'
|
|
226
|
+
: past
|
|
227
|
+
? 'manage-subscription-row is-past'
|
|
228
|
+
: 'manage-subscription-row';
|
|
229
|
+
return (_jsxs("section", { className: 'manage-subscription-section', "aria-label": title, children: [_jsx("h2", { children: title }), description ? _jsx("p", { className: 'manage-subscription-section-note', children: description }) : null, _jsx("ul", { className: 'manage-subscription-list', role: 'list', children: subscriptions.map((subscription) => (_jsx("li", { children: _jsxs("button", { type: 'button', className: rowClassName, disabled: isDisabled, onClick: () => onSelect(subscription), children: [_jsx("span", { className: 'manage-subscription-radio', "aria-hidden": true }), _jsxs("span", { className: 'manage-subscription-row-heading', children: [_jsxs("span", { className: 'manage-subscription-row-title', children: ["Subscription #", formatSubscriptionNumber(subscription)] }), _jsx("span", { className: 'manage-subscription-row-status', children: resolveSubscriptionStatusLabel(subscription) })] }), _jsx("span", { className: 'manage-subscription-row-plan', children: resolvePlanLine(subscription, plans) }), _jsxs("span", { className: 'manage-subscription-row-meta', children: [_jsx("span", { className: cancelled ? 'is-active-until' : undefined, children: cancelled
|
|
230
|
+
? resolveBillingDateLabel(subscription, { cancelled })
|
|
231
|
+
: resolveBillingDateLabel(subscription, { past }) }), _jsxs("span", { children: ["Created at: ", resolveCreatedDate(subscription)] })] })] }) }, subscription.id))) })] }));
|
|
209
232
|
}
|
|
210
233
|
const manageSubscriptionStyles = `
|
|
211
234
|
.manage-subscription {
|
|
@@ -295,6 +318,11 @@ const manageSubscriptionStyles = `
|
|
|
295
318
|
opacity: 0.82;
|
|
296
319
|
}
|
|
297
320
|
|
|
321
|
+
.manage-subscription-row.is-cancelled {
|
|
322
|
+
border-left-color: var(--color-warning, #f28100);
|
|
323
|
+
cursor: default;
|
|
324
|
+
}
|
|
325
|
+
|
|
298
326
|
.manage-subscription-radio {
|
|
299
327
|
width: 14px;
|
|
300
328
|
height: 14px;
|
|
@@ -303,12 +331,31 @@ const manageSubscriptionStyles = `
|
|
|
303
331
|
display: block;
|
|
304
332
|
}
|
|
305
333
|
|
|
334
|
+
.manage-subscription-row-heading {
|
|
335
|
+
width: 100%;
|
|
336
|
+
display: flex;
|
|
337
|
+
align-items: center;
|
|
338
|
+
justify-content: space-between;
|
|
339
|
+
gap: 8px;
|
|
340
|
+
}
|
|
341
|
+
|
|
306
342
|
.manage-subscription-row-title {
|
|
307
343
|
font-size: 15px;
|
|
308
344
|
line-height: 1.25;
|
|
309
345
|
font-weight: 800;
|
|
310
346
|
}
|
|
311
347
|
|
|
348
|
+
.manage-subscription-row-status {
|
|
349
|
+
border-radius: 999px;
|
|
350
|
+
background: color-mix(in srgb, var(--color-warning, #f28100) 16%, var(--color-surface, #fff) 84%);
|
|
351
|
+
color: var(--color-warning, #f28100);
|
|
352
|
+
padding: 3px 8px;
|
|
353
|
+
font-size: 10px;
|
|
354
|
+
line-height: 1.2;
|
|
355
|
+
font-weight: 800;
|
|
356
|
+
text-transform: uppercase;
|
|
357
|
+
}
|
|
358
|
+
|
|
312
359
|
.manage-subscription-row-plan {
|
|
313
360
|
font-size: 13px;
|
|
314
361
|
line-height: 1.2;
|
|
@@ -326,6 +373,11 @@ const manageSubscriptionStyles = `
|
|
|
326
373
|
line-height: 1.3;
|
|
327
374
|
}
|
|
328
375
|
|
|
376
|
+
.manage-subscription-row-meta .is-active-until {
|
|
377
|
+
color: var(--color-text, #262729);
|
|
378
|
+
font-weight: 800;
|
|
379
|
+
}
|
|
380
|
+
|
|
329
381
|
.manage-subscription-support {
|
|
330
382
|
margin: 30px 0 0;
|
|
331
383
|
color: var(--color-text, #262729);
|
|
@@ -371,6 +423,19 @@ const manageSubscriptionStyles = `
|
|
|
371
423
|
font-weight: 800;
|
|
372
424
|
}
|
|
373
425
|
|
|
426
|
+
.manage-subscription-done-status {
|
|
427
|
+
width: fit-content;
|
|
428
|
+
margin: 14px auto 0;
|
|
429
|
+
border-radius: 999px;
|
|
430
|
+
background: color-mix(in srgb, var(--color-warning, #f28100) 16%, var(--color-surface, #fff) 84%);
|
|
431
|
+
color: var(--color-warning, #f28100);
|
|
432
|
+
padding: 4px 10px;
|
|
433
|
+
font-size: 11px;
|
|
434
|
+
line-height: 1.2;
|
|
435
|
+
font-weight: 800;
|
|
436
|
+
text-transform: uppercase;
|
|
437
|
+
}
|
|
438
|
+
|
|
374
439
|
.manage-subscription-reasons {
|
|
375
440
|
margin: 18px 0 0;
|
|
376
441
|
padding: 0;
|
|
@@ -446,4 +511,5 @@ const manageSubscriptionStyles = `
|
|
|
446
511
|
`;
|
|
447
512
|
export const __manageSubscriptionScreenTestables = {
|
|
448
513
|
resolveBillingDateLabel,
|
|
514
|
+
resolveSubscriptionStatusLabel,
|
|
449
515
|
};
|