@funnelsgrove/cli 0.1.231 → 0.1.237

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.
Files changed (35) hide show
  1. package/README.md +11 -0
  2. package/dist/cli.js +10 -0
  3. package/dist/localeContentSource.d.ts +13 -0
  4. package/dist/localeContentSource.js +117 -0
  5. package/dist/localeFilePlan.d.ts +4 -0
  6. package/dist/localeFilePlan.js +63 -0
  7. package/dist/localeModuleEdits.d.ts +19 -0
  8. package/dist/localeModuleEdits.js +97 -0
  9. package/dist/locales.d.ts +6 -0
  10. package/dist/locales.js +92 -0
  11. package/funnel-contract-compatibility.json +38 -38
  12. package/package.json +4 -4
  13. package/template_docs/.funnelsgrove-docs.json +5 -5
  14. package/template_docs/AGENTS.md +1 -0
  15. package/template_docs/docs/funnelsgrove/contracts/content-answers.md +69 -0
  16. package/template_docs/docs/funnelsgrove/migrations/step-contract-v3.md +1 -1
  17. package/template_docs/funnel-docs.config.json +1 -1
  18. package/template_scaffold/.funnelsgrove-docs.json +5 -5
  19. package/template_scaffold/.funnelsgrove-scaffold.json +35 -15
  20. package/template_scaffold/AGENTS.md +1 -0
  21. package/template_scaffold/docs/funnelsgrove/contracts/content-answers.md +69 -0
  22. package/template_scaffold/docs/funnelsgrove/migrations/step-contract-v3.md +1 -1
  23. package/template_scaffold/funnel-docs.config.json +1 -1
  24. package/template_scaffold/package-lock.json +15 -15
  25. package/template_scaffold/package.json +3 -3
  26. package/template_scaffold/src/app/subscription-dashboard/page.tsx +22 -19
  27. package/template_scaffold/src/components/FunnelFlow.tsx +11 -5
  28. package/template_scaffold/src/components/paywall/PaywallRenewalDisclaimer.tsx +23 -0
  29. package/template_scaffold/src/components/paywall/localize-paywall-plan-labels.ts +15 -0
  30. package/template_scaffold/src/config/billing.plans.ts +11 -10
  31. package/template_scaffold/src/config/billing.test.plans.ts +11 -10
  32. package/template_scaffold/src/steps/content/runtime.content.ts +22 -0
  33. package/template_scaffold/src/steps/content/step-32-paywall.content.ts +41 -1
  34. package/template_scaffold/src/steps/content/subscription-dashboard.content.ts +54 -0
  35. package/template_scaffold/src/steps/step-32-paywall.tsx +12 -22
@@ -0,0 +1,54 @@
1
+ import type { LocalizedStepContent } from '@funnelsgrove/runtime';
2
+
3
+ export 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 || 'Selected plan';
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={`Time left ${String(minutes).padStart(2, '0')} minutes ${String(seconds).padStart(2, '0')} seconds`}
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}% OFF
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='Plans'>
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'>{plan.perDayLabel}</p>
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
- <p className='paywall-v2-disclaimer'>{paymentDisclaimer}</p>
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 || 'refund policy'}
1219
+ {content.guarantee.links[0]?.label || content.refundPolicyLabel}
1230
1220
  </a>{' '}
1231
- for details and limitations.
1221
+ {content.guarantee.detailsSuffix}
1232
1222
  </p>
1233
1223
  </div>
1234
1224
  </section>