@funnelsgrove/cli 0.1.231 → 0.1.233
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/README.md +9 -0
- package/dist/cli.js +10 -0
- package/dist/locales.d.ts +6 -0
- package/dist/locales.js +126 -0
- package/funnel-contract-compatibility.json +32 -32
- package/package.json +4 -4
- package/template_docs/.funnelsgrove-docs.json +5 -5
- package/template_docs/AGENTS.md +1 -0
- package/template_docs/docs/funnelsgrove/contracts/content-answers.md +50 -0
- package/template_docs/docs/funnelsgrove/migrations/step-contract-v3.md +1 -1
- package/template_docs/funnel-docs.config.json +1 -1
- package/template_scaffold/.funnelsgrove-docs.json +5 -5
- package/template_scaffold/.funnelsgrove-scaffold.json +35 -15
- package/template_scaffold/AGENTS.md +1 -0
- package/template_scaffold/docs/funnelsgrove/contracts/content-answers.md +50 -0
- package/template_scaffold/docs/funnelsgrove/migrations/step-contract-v3.md +1 -1
- package/template_scaffold/funnel-docs.config.json +1 -1
- package/template_scaffold/package-lock.json +15 -15
- package/template_scaffold/package.json +3 -3
- package/template_scaffold/src/app/subscription-dashboard/page.tsx +22 -19
- package/template_scaffold/src/components/FunnelFlow.tsx +11 -5
- package/template_scaffold/src/components/paywall/PaywallRenewalDisclaimer.tsx +23 -0
- package/template_scaffold/src/components/paywall/localize-paywall-plan-labels.ts +15 -0
- package/template_scaffold/src/config/billing.plans.ts +11 -10
- package/template_scaffold/src/config/billing.test.plans.ts +11 -10
- package/template_scaffold/src/steps/content/runtime.content.ts +22 -0
- package/template_scaffold/src/steps/content/step-32-paywall.content.ts +41 -1
- package/template_scaffold/src/steps/content/subscription-dashboard.content.ts +54 -0
- package/template_scaffold/src/steps/step-32-paywall.tsx +12 -22
|
@@ -7,11 +7,15 @@ import type {
|
|
|
7
7
|
StepImage,
|
|
8
8
|
} from '@funnelsgrove/runtime';
|
|
9
9
|
|
|
10
|
-
type StepPaywallLocaleContent = {
|
|
10
|
+
export type StepPaywallLocaleContent = {
|
|
11
|
+
catalogLabels: Record<string, string>;
|
|
11
12
|
loadingLabel: string;
|
|
12
13
|
topBarDiscountLabel: string;
|
|
13
14
|
topBarCtaLabel: string;
|
|
14
15
|
countdownUnits: readonly string[];
|
|
16
|
+
countdownAriaTemplate: string;
|
|
17
|
+
selectedPlanLabel: string;
|
|
18
|
+
refundPolicyLabel: string;
|
|
15
19
|
hero: {
|
|
16
20
|
pills: readonly string[];
|
|
17
21
|
image: StepImage;
|
|
@@ -30,6 +34,7 @@ type StepPaywallLocaleContent = {
|
|
|
30
34
|
cardButtonLabel: string;
|
|
31
35
|
metaLabels: readonly string[];
|
|
32
36
|
stripeUnavailableNote: string;
|
|
37
|
+
perDayLabel: string;
|
|
33
38
|
};
|
|
34
39
|
checkout: {
|
|
35
40
|
title: string;
|
|
@@ -67,6 +72,11 @@ type StepPaywallLocaleContent = {
|
|
|
67
72
|
legalLinks: readonly LinkItem[];
|
|
68
73
|
supportText: string;
|
|
69
74
|
renewalTemplate: string;
|
|
75
|
+
renewalConsentTemplate: string;
|
|
76
|
+
discountedIntroLabel: string;
|
|
77
|
+
selectedIntroLabel: string;
|
|
78
|
+
cadenceFallback: string;
|
|
79
|
+
cadence: Record<'day' | 'week' | 'month' | 'year', { one: string; many: string }>;
|
|
70
80
|
specialOffer: {
|
|
71
81
|
image: StepImage;
|
|
72
82
|
discountLabel: string;
|
|
@@ -94,6 +104,7 @@ type StepPaywallLocaleContent = {
|
|
|
94
104
|
title: string;
|
|
95
105
|
description: string;
|
|
96
106
|
links: readonly LinkItem[];
|
|
107
|
+
detailsSuffix: string;
|
|
97
108
|
};
|
|
98
109
|
};
|
|
99
110
|
|
|
@@ -101,10 +112,27 @@ export const stepPaywallContent = {
|
|
|
101
112
|
defaultLocale: 'en',
|
|
102
113
|
locales: {
|
|
103
114
|
en: {
|
|
115
|
+
catalogLabels: {
|
|
116
|
+
"1-Week Plan": "1-Week Plan",
|
|
117
|
+
"1-Month Plan": "1-Month Plan",
|
|
118
|
+
"1-Year Plan": "1-Year Plan",
|
|
119
|
+
"🔥 Most Popular Choice": "🔥 Most Popular Choice",
|
|
120
|
+
"$7.99/week": "$7.99/week",
|
|
121
|
+
"$9.99/week": "$9.99/week",
|
|
122
|
+
"$29.99/month": "$29.99/month",
|
|
123
|
+
"$59.99/year": "$59.99/year",
|
|
124
|
+
"$7.99 today": "$7.99 today",
|
|
125
|
+
"$9.99 today": "$9.99 today",
|
|
126
|
+
"$29.99 today": "$29.99 today",
|
|
127
|
+
"$59.99 today": "$59.99 today",
|
|
128
|
+
},
|
|
104
129
|
loadingLabel: 'Loading...',
|
|
105
130
|
topBarDiscountLabel: '⏰ Discount Expires in',
|
|
106
131
|
topBarCtaLabel: 'GET MY PLAN',
|
|
107
132
|
countdownUnits: ['minutes', 'seconds'],
|
|
133
|
+
countdownAriaTemplate: 'Time left {minutes} minutes {seconds} seconds',
|
|
134
|
+
selectedPlanLabel: 'Selected plan',
|
|
135
|
+
refundPolicyLabel: 'refund policy',
|
|
108
136
|
hero: {
|
|
109
137
|
pills: ['Find hidden money and', "claim what's yours"],
|
|
110
138
|
image: {
|
|
@@ -124,6 +152,7 @@ export const stepPaywallContent = {
|
|
|
124
152
|
},
|
|
125
153
|
plans: {
|
|
126
154
|
title: 'Choose your plan',
|
|
155
|
+
perDayLabel: 'per day',
|
|
127
156
|
cardButtonLabel: 'GET MY PLAN',
|
|
128
157
|
metaLabels: ['Money-back guarantee', 'Cancel anytime'],
|
|
129
158
|
stripeUnavailableNote:
|
|
@@ -180,6 +209,16 @@ export const stepPaywallContent = {
|
|
|
180
209
|
},
|
|
181
210
|
],
|
|
182
211
|
supportText: 'Need help? Contact us at',
|
|
212
|
+
renewalConsentTemplate: 'Without cancellation, before the {intro} ends, I accept that the app will automatically charge {amount} {cadence} until I cancel. Cancel online via the profile on the website or app.',
|
|
213
|
+
discountedIntroLabel: 'discounted intro plan',
|
|
214
|
+
selectedIntroLabel: 'selected plan',
|
|
215
|
+
cadenceFallback: 'on your selected billing schedule',
|
|
216
|
+
cadence: {
|
|
217
|
+
day: { one: 'every day', many: 'every {count} days' },
|
|
218
|
+
week: { one: 'every week', many: 'every {count} weeks' },
|
|
219
|
+
month: { one: 'every month', many: 'every {count} months' },
|
|
220
|
+
year: { one: 'every year', many: 'every {count} years' },
|
|
221
|
+
},
|
|
183
222
|
renewalTemplate:
|
|
184
223
|
"You'll be charged {amount} today for your selected plan. Your subscription renews automatically unless cancelled at least 24 hours before renewal date.",
|
|
185
224
|
specialOffer: {
|
|
@@ -302,6 +341,7 @@ export const stepPaywallContent = {
|
|
|
302
341
|
],
|
|
303
342
|
},
|
|
304
343
|
guarantee: {
|
|
344
|
+
detailsSuffix: 'for details and limitations.',
|
|
305
345
|
image: {
|
|
306
346
|
src: '/paywall/money-back-badge.jpg',
|
|
307
347
|
alt: '30 day money back guarantee badge',
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { LocalizedStepContent } from '@funnelsgrove/runtime';
|
|
2
|
+
|
|
3
|
+
type SubscriptionDashboardContent = {
|
|
4
|
+
brand: string;
|
|
5
|
+
title: string;
|
|
6
|
+
description: string;
|
|
7
|
+
loading: string;
|
|
8
|
+
userId: string;
|
|
9
|
+
email: string;
|
|
10
|
+
status: string;
|
|
11
|
+
events: string;
|
|
12
|
+
notProvided: string;
|
|
13
|
+
unknown: string;
|
|
14
|
+
empty: string;
|
|
15
|
+
cancelAtEnd: string;
|
|
16
|
+
autoRenew: string;
|
|
17
|
+
periodEnds: string;
|
|
18
|
+
updating: string;
|
|
19
|
+
renew: string;
|
|
20
|
+
cancel: string;
|
|
21
|
+
refresh: string;
|
|
22
|
+
loadError: string;
|
|
23
|
+
cancelError: string;
|
|
24
|
+
renewError: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const subscriptionDashboardContent = {
|
|
28
|
+
defaultLocale: 'en',
|
|
29
|
+
locales: {
|
|
30
|
+
en: {
|
|
31
|
+
"brand": "App Deals",
|
|
32
|
+
"title": "Manage Subscription",
|
|
33
|
+
"description": "View active plans and manage cancellation or renewal.",
|
|
34
|
+
"loading": "Loading subscription data...",
|
|
35
|
+
"userId": "User ID:",
|
|
36
|
+
"email": "Email:",
|
|
37
|
+
"status": "Status:",
|
|
38
|
+
"events": "Tracked events:",
|
|
39
|
+
"notProvided": "Not provided",
|
|
40
|
+
"unknown": "unknown",
|
|
41
|
+
"empty": "No subscriptions found for this user.",
|
|
42
|
+
"cancelAtEnd": "Will cancel at period end",
|
|
43
|
+
"autoRenew": "Auto-renew is active",
|
|
44
|
+
"periodEnds": "Current period ends:",
|
|
45
|
+
"updating": "Updating...",
|
|
46
|
+
"renew": "Renew",
|
|
47
|
+
"cancel": "Cancel",
|
|
48
|
+
"refresh": "Refresh",
|
|
49
|
+
"loadError": "Failed to load subscriptions",
|
|
50
|
+
"cancelError": "Failed to cancel subscription",
|
|
51
|
+
"renewError": "Failed to renew subscription"
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
} as const satisfies LocalizedStepContent<SubscriptionDashboardContent>;
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState, type FormEvent } from 'react';
|
|
4
4
|
import Image from 'next/image';
|
|
5
|
+
import { localizePaywallPlanLabels } from '@/components/paywall/localize-paywall-plan-labels';
|
|
6
|
+
import { PaywallRenewalDisclaimer } from '@/components/paywall/PaywallRenewalDisclaimer';
|
|
5
7
|
import { checkoutRuntimeConfig } from '@/runtime/checkout-runtime-config';
|
|
6
8
|
import {
|
|
7
9
|
billingDiscountStorageKeys,
|
|
@@ -33,7 +35,6 @@ import {
|
|
|
33
35
|
advancePaywallDiscountState,
|
|
34
36
|
buildBillingDiscountCatalog,
|
|
35
37
|
buildDiscountedPaywallPlans,
|
|
36
|
-
buildPaywallRenewalDisclaimer,
|
|
37
38
|
getDefaultPlanId,
|
|
38
39
|
getPaywallPromoDisplayName,
|
|
39
40
|
resolvePaywallDiscountState,
|
|
@@ -226,8 +227,8 @@ function StepPaywallRuntime({
|
|
|
226
227
|
plans: availablePlans,
|
|
227
228
|
couponId: activeCouponId,
|
|
228
229
|
discountPercent: discountState.discountPercent,
|
|
229
|
-
}),
|
|
230
|
-
[activeCouponId, availablePlans, discountState.discountPercent],
|
|
230
|
+
}).map((plan) => localizePaywallPlanLabels(plan, content.catalogLabels)),
|
|
231
|
+
[activeCouponId, availablePlans, discountState.discountPercent, content.catalogLabels],
|
|
231
232
|
);
|
|
232
233
|
const selectedPlan = useMemo(() => {
|
|
233
234
|
if (availablePlans.length === 0) {
|
|
@@ -252,7 +253,7 @@ function StepPaywallRuntime({
|
|
|
252
253
|
return displayPlans.find((plan) => plan.id === selectedPlanId) ?? displayPlans[0];
|
|
253
254
|
}, [displayPlans, initialPlanId, selectedPlanId]);
|
|
254
255
|
const checkoutSummaryLabel =
|
|
255
|
-
selectedDisplayPlan?.checkoutSummaryLabel?.trim() || selectedDisplayPlan?.title ||
|
|
256
|
+
selectedDisplayPlan?.checkoutSummaryLabel?.trim() || selectedDisplayPlan?.title || content.selectedPlanLabel;
|
|
256
257
|
const checkoutProvider = activeBillingRuntime.provider;
|
|
257
258
|
const storyOptions = useMemo<readonly StoryOption[]>(
|
|
258
259
|
() =>
|
|
@@ -286,17 +287,6 @@ function StepPaywallRuntime({
|
|
|
286
287
|
|
|
287
288
|
return getPaywallPromoDisplayName(activeDiscountStartedAtMs);
|
|
288
289
|
}, [activeDiscountStartedAtMs, discountState.status]);
|
|
289
|
-
const paymentDisclaimer = useMemo(() => {
|
|
290
|
-
if (!selectedPlan) {
|
|
291
|
-
return '';
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return buildPaywallRenewalDisclaimer({
|
|
295
|
-
plan: selectedPlan,
|
|
296
|
-
hasActiveDiscount: Boolean(activeCouponId),
|
|
297
|
-
productName: 'the app',
|
|
298
|
-
});
|
|
299
|
-
}, [activeCouponId, selectedPlan]);
|
|
300
290
|
const appliedDiscountAmountCents = useMemo(() => {
|
|
301
291
|
if (!selectedDisplayPlan) {
|
|
302
292
|
return 0;
|
|
@@ -934,7 +924,7 @@ function StepPaywallRuntime({
|
|
|
934
924
|
<div className='paywall-v2-top-controls'>
|
|
935
925
|
<div
|
|
936
926
|
className='paywall-v2-countdown'
|
|
937
|
-
aria-label={
|
|
927
|
+
aria-label={content.countdownAriaTemplate.replace('{minutes}', String(minutes).padStart(2, '0')).replace('{seconds}', String(seconds).padStart(2, '0'))}
|
|
938
928
|
>
|
|
939
929
|
<p className='paywall-v2-countdown-value'>
|
|
940
930
|
<span className='paywall-v2-countdown-segment'>
|
|
@@ -1011,7 +1001,7 @@ function StepPaywallRuntime({
|
|
|
1011
1001
|
</span>
|
|
1012
1002
|
) : null}
|
|
1013
1003
|
<span className='paywall-v2-promo-upgrade-badge-main'>
|
|
1014
|
-
{discountState.discountPercent}
|
|
1004
|
+
{content.checkout.discountLabelTemplate.replace('{percent}', String(discountState.discountPercent))}
|
|
1015
1005
|
</span>
|
|
1016
1006
|
</div>
|
|
1017
1007
|
<p className='paywall-v2-promo-upgrade-label'>{content.promo.upgradedLabel}</p>
|
|
@@ -1041,7 +1031,7 @@ function StepPaywallRuntime({
|
|
|
1041
1031
|
<p className='paywall-v2-highlight-copy'>{content.highlight.description}</p>
|
|
1042
1032
|
</section>
|
|
1043
1033
|
|
|
1044
|
-
<section id='paywall-v2-plans' className='paywall-v2-plan-list' aria-label=
|
|
1034
|
+
<section id='paywall-v2-plans' className='paywall-v2-plan-list' aria-label={content.plans.title}>
|
|
1045
1035
|
<h2 className='paywall-v2-title'>{content.plans.title}</h2>
|
|
1046
1036
|
|
|
1047
1037
|
{displayPlans.map((plan) => (
|
|
@@ -1089,7 +1079,7 @@ function StepPaywallRuntime({
|
|
|
1089
1079
|
) : null}
|
|
1090
1080
|
<span className='paywall-v2-plan-per-day-amount'>{plan.perDayAmount}</span>
|
|
1091
1081
|
</p>
|
|
1092
|
-
<p className='paywall-v2-plan-per-day-label'>{
|
|
1082
|
+
<p className='paywall-v2-plan-per-day-label'>{content.plans.perDayLabel}</p>
|
|
1093
1083
|
</div>
|
|
1094
1084
|
</div>
|
|
1095
1085
|
</div>
|
|
@@ -1130,7 +1120,7 @@ function StepPaywallRuntime({
|
|
|
1130
1120
|
{content.plans.cardButtonLabel}
|
|
1131
1121
|
</button>
|
|
1132
1122
|
<p className='paywall-v2-alt-meta'>{content.plans.metaLabels.join(' · ')}</p>
|
|
1133
|
-
<
|
|
1123
|
+
<PaywallRenewalDisclaimer plan={selectedPlan} hasActiveDiscount={Boolean(activeCouponId)} copy={content.checkout} catalogLabels={content.catalogLabels} />
|
|
1134
1124
|
<p className='paywall-v2-support'>
|
|
1135
1125
|
{content.checkout.supportText}{' '}
|
|
1136
1126
|
<a href={`mailto:${supportEmail}`}>{supportEmail}</a>
|
|
@@ -1226,9 +1216,9 @@ function StepPaywallRuntime({
|
|
|
1226
1216
|
<p className='paywall-v2-guarantee-text'>
|
|
1227
1217
|
{content.guarantee.description}{' '}
|
|
1228
1218
|
<a href={content.guarantee.links[0]?.href || '/refund-policy'}>
|
|
1229
|
-
{content.guarantee.links[0]?.label ||
|
|
1219
|
+
{content.guarantee.links[0]?.label || content.refundPolicyLabel}
|
|
1230
1220
|
</a>{' '}
|
|
1231
|
-
|
|
1221
|
+
{content.guarantee.detailsSuffix}
|
|
1232
1222
|
</p>
|
|
1233
1223
|
</div>
|
|
1234
1224
|
</section>
|