@sudobility/building_blocks 0.0.154 → 0.0.156
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/components/subscription/index.d.ts +0 -4
- package/dist/index.d.ts +0 -4
- package/dist/index.js +3 -729
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/components/subscription/AppPricingPage.d.ts +0 -79
- package/dist/components/subscription/AppSubscriptionsPage.d.ts +0 -101
package/dist/index.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import { b, a, S, u } from "./SafeSubscriptionContext-CNuEKeqJ.js";
|
|
2
|
-
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
3
|
-
import React, { useState, useRef, useMemo, useCallback, useEffect } from "react";
|
|
4
|
-
import { SubscriptionLayout, SubscriptionTile, SegmentedControl } from "@sudobility/subscription-components";
|
|
5
|
-
import { useSubscriptionPeriods, useSubscriptionForPeriod, useSubscribable, useUserSubscription, refreshSubscription } from "@sudobility/subscription_lib";
|
|
6
|
-
import { TopbarProvider, Topbar, TopbarLeft, TopbarNavigation, TopbarLogo, Logo, TopbarCenter, TopbarRight, TopbarActions, TopbarMobileContent, Footer, FooterCompact, FooterCompactLeft, FooterVersion, FooterCopyright, FooterCompactRight, FooterGrid, FooterLinkSection, FooterLink, FooterBottom, FooterBrand, FooterSocialLinks, LayoutProvider, AspectFitView, Label, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MasterListItem, Section, MasterDetailLayout } from "@sudobility/components";
|
|
7
2
|
import { clsx } from "clsx";
|
|
8
3
|
import { twMerge } from "tailwind-merge";
|
|
9
4
|
import i18n from "i18next";
|
|
@@ -11,6 +6,9 @@ import { default as default2 } from "i18next";
|
|
|
11
6
|
import { initReactI18next } from "react-i18next";
|
|
12
7
|
import Backend from "i18next-http-backend";
|
|
13
8
|
import LanguageDetector from "i18next-browser-languagedetector";
|
|
9
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
10
|
+
import React, { useState, useRef, useMemo, useCallback, useEffect } from "react";
|
|
11
|
+
import { TopbarProvider, Topbar, TopbarLeft, TopbarNavigation, TopbarLogo, Logo, TopbarCenter, TopbarRight, TopbarActions, TopbarMobileContent, Footer, FooterCompact, FooterCompactLeft, FooterVersion, FooterCopyright, FooterCompactRight, FooterGrid, FooterLinkSection, FooterLink, FooterBottom, FooterBrand, FooterSocialLinks, LayoutProvider, AspectFitView, Label, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MasterListItem, Section, MasterDetailLayout } from "@sudobility/components";
|
|
14
12
|
import { ChevronDownIcon, CalendarDaysIcon, PaintBrushIcon, LanguageIcon, ChevronRightIcon, EnvelopeIcon, DocumentTextIcon, CogIcon, HomeIcon } from "@heroicons/react/24/outline";
|
|
15
13
|
import { GRADIENT_CLASSES, textVariants } from "@sudobility/design";
|
|
16
14
|
import { cva } from "class-variance-authority";
|
|
@@ -2023,728 +2021,6 @@ function LoginPage({
|
|
|
2023
2021
|
}
|
|
2024
2022
|
);
|
|
2025
2023
|
}
|
|
2026
|
-
function AppSubscriptionsPage({
|
|
2027
|
-
offerId,
|
|
2028
|
-
rateLimitsConfig,
|
|
2029
|
-
labels,
|
|
2030
|
-
formatters,
|
|
2031
|
-
onPurchase,
|
|
2032
|
-
onRestore,
|
|
2033
|
-
onPurchaseSuccess,
|
|
2034
|
-
onRestoreSuccess,
|
|
2035
|
-
onError,
|
|
2036
|
-
onWarning,
|
|
2037
|
-
onTrack
|
|
2038
|
-
}) {
|
|
2039
|
-
const {
|
|
2040
|
-
periods,
|
|
2041
|
-
isLoading: periodsLoading,
|
|
2042
|
-
error: periodsError
|
|
2043
|
-
} = useSubscriptionPeriods(offerId);
|
|
2044
|
-
const [selectedPeriod, setSelectedPeriod] = useState("monthly");
|
|
2045
|
-
useEffect(() => {
|
|
2046
|
-
if (periods.length > 0 && !periods.includes(selectedPeriod)) {
|
|
2047
|
-
setSelectedPeriod(periods[0]);
|
|
2048
|
-
}
|
|
2049
|
-
}, [periods, selectedPeriod]);
|
|
2050
|
-
const {
|
|
2051
|
-
packages,
|
|
2052
|
-
isLoading: packagesLoading,
|
|
2053
|
-
error: packagesError
|
|
2054
|
-
} = useSubscriptionForPeriod(offerId, selectedPeriod);
|
|
2055
|
-
const {
|
|
2056
|
-
subscribablePackageIds,
|
|
2057
|
-
isLoading: subscribableLoading,
|
|
2058
|
-
error: subscribableError
|
|
2059
|
-
} = useSubscribable(offerId);
|
|
2060
|
-
const {
|
|
2061
|
-
subscription: currentSubscription,
|
|
2062
|
-
isLoading: subscriptionLoading,
|
|
2063
|
-
error: subscriptionError,
|
|
2064
|
-
update: updateSubscription
|
|
2065
|
-
} = useUserSubscription();
|
|
2066
|
-
useEffect(() => {
|
|
2067
|
-
updateSubscription();
|
|
2068
|
-
}, [updateSubscription]);
|
|
2069
|
-
const isLoading = periodsLoading || packagesLoading || subscribableLoading || subscriptionLoading;
|
|
2070
|
-
const error = periodsError || packagesError || subscribableError || subscriptionError;
|
|
2071
|
-
const [selectedPlan, setSelectedPlan] = useState(null);
|
|
2072
|
-
const [isPurchasing, setIsPurchasing] = useState(false);
|
|
2073
|
-
const [isRestoring, setIsRestoring] = useState(false);
|
|
2074
|
-
const track = useCallback(
|
|
2075
|
-
(label, params) => {
|
|
2076
|
-
onTrack == null ? void 0 : onTrack({
|
|
2077
|
-
eventType: "subscription_action",
|
|
2078
|
-
componentName: "AppSubscriptionsPage",
|
|
2079
|
-
label,
|
|
2080
|
-
params
|
|
2081
|
-
});
|
|
2082
|
-
},
|
|
2083
|
-
[onTrack]
|
|
2084
|
-
);
|
|
2085
|
-
const handlePeriodChange = useCallback(
|
|
2086
|
-
(value) => {
|
|
2087
|
-
const newPeriod = value;
|
|
2088
|
-
setSelectedPeriod(newPeriod);
|
|
2089
|
-
setSelectedPlan(null);
|
|
2090
|
-
track("billing_period_changed", { billing_period: newPeriod });
|
|
2091
|
-
},
|
|
2092
|
-
[track]
|
|
2093
|
-
);
|
|
2094
|
-
const getPeriodLabel = useCallback(
|
|
2095
|
-
(period) => {
|
|
2096
|
-
switch (period) {
|
|
2097
|
-
case "yearly":
|
|
2098
|
-
return labels.periodYear;
|
|
2099
|
-
case "monthly":
|
|
2100
|
-
return labels.periodMonth;
|
|
2101
|
-
case "weekly":
|
|
2102
|
-
return labels.periodWeek;
|
|
2103
|
-
default:
|
|
2104
|
-
return period;
|
|
2105
|
-
}
|
|
2106
|
-
},
|
|
2107
|
-
[labels]
|
|
2108
|
-
);
|
|
2109
|
-
const getYearlySavingsPercent = useCallback(
|
|
2110
|
-
(yearlyPackage) => {
|
|
2111
|
-
if (!yearlyPackage.product) return void 0;
|
|
2112
|
-
const monthlyPackageId = yearlyPackage.packageId.replace(
|
|
2113
|
-
"_yearly",
|
|
2114
|
-
"_monthly"
|
|
2115
|
-
);
|
|
2116
|
-
const monthlyPkg = packages.find((p) => p.packageId === monthlyPackageId);
|
|
2117
|
-
if (!(monthlyPkg == null ? void 0 : monthlyPkg.product)) return void 0;
|
|
2118
|
-
const yearlyPrice = yearlyPackage.product.price;
|
|
2119
|
-
const monthlyPrice = monthlyPkg.product.price;
|
|
2120
|
-
if (monthlyPrice <= 0 || yearlyPrice <= 0) return void 0;
|
|
2121
|
-
const annualizedMonthly = monthlyPrice * 12;
|
|
2122
|
-
const savings = (annualizedMonthly - yearlyPrice) / annualizedMonthly * 100;
|
|
2123
|
-
return Math.round(savings);
|
|
2124
|
-
},
|
|
2125
|
-
[packages]
|
|
2126
|
-
);
|
|
2127
|
-
const billingPeriodOptions = useMemo(() => {
|
|
2128
|
-
return periods.map((period) => ({
|
|
2129
|
-
value: period,
|
|
2130
|
-
label: period === "monthly" ? labels.billingMonthly : labels.billingYearly
|
|
2131
|
-
}));
|
|
2132
|
-
}, [periods, labels]);
|
|
2133
|
-
const isCurrentPlan = useCallback(
|
|
2134
|
-
(packageId, productId) => {
|
|
2135
|
-
if (!(currentSubscription == null ? void 0 : currentSubscription.isActive)) return false;
|
|
2136
|
-
return productId === currentSubscription.productId || packageId === currentSubscription.packageId;
|
|
2137
|
-
},
|
|
2138
|
-
[currentSubscription]
|
|
2139
|
-
);
|
|
2140
|
-
const isPackageEnabled = useCallback(
|
|
2141
|
-
(packageId) => {
|
|
2142
|
-
if (subscribableLoading || subscribablePackageIds.length === 0)
|
|
2143
|
-
return true;
|
|
2144
|
-
return subscribablePackageIds.includes(packageId);
|
|
2145
|
-
},
|
|
2146
|
-
[subscribableLoading, subscribablePackageIds]
|
|
2147
|
-
);
|
|
2148
|
-
const canUpgradeTo = useCallback(
|
|
2149
|
-
(packageId, productId) => {
|
|
2150
|
-
return isPackageEnabled(packageId) && !isCurrentPlan(packageId, productId);
|
|
2151
|
-
},
|
|
2152
|
-
[isPackageEnabled, isCurrentPlan]
|
|
2153
|
-
);
|
|
2154
|
-
useEffect(() => {
|
|
2155
|
-
if ((currentSubscription == null ? void 0 : currentSubscription.isActive) && currentSubscription.packageId) {
|
|
2156
|
-
setSelectedPlan(currentSubscription.packageId);
|
|
2157
|
-
if (currentSubscription.period && periods.includes(currentSubscription.period)) {
|
|
2158
|
-
setSelectedPeriod(currentSubscription.period);
|
|
2159
|
-
}
|
|
2160
|
-
}
|
|
2161
|
-
}, [currentSubscription, periods]);
|
|
2162
|
-
const handlePlanSelect = useCallback(
|
|
2163
|
-
(planIdentifier) => {
|
|
2164
|
-
setSelectedPlan(planIdentifier);
|
|
2165
|
-
track("plan_selected", {
|
|
2166
|
-
plan_identifier: planIdentifier ?? "free",
|
|
2167
|
-
is_free_tier: planIdentifier === null
|
|
2168
|
-
});
|
|
2169
|
-
},
|
|
2170
|
-
[track]
|
|
2171
|
-
);
|
|
2172
|
-
const handlePurchase = useCallback(async () => {
|
|
2173
|
-
if (!selectedPlan) return;
|
|
2174
|
-
setIsPurchasing(true);
|
|
2175
|
-
track("purchase_initiated", { plan_identifier: selectedPlan });
|
|
2176
|
-
try {
|
|
2177
|
-
const result = await onPurchase(selectedPlan);
|
|
2178
|
-
if (result) {
|
|
2179
|
-
await refreshSubscription();
|
|
2180
|
-
track("purchase_completed", { plan_identifier: selectedPlan });
|
|
2181
|
-
onPurchaseSuccess == null ? void 0 : onPurchaseSuccess();
|
|
2182
|
-
setSelectedPlan(null);
|
|
2183
|
-
} else {
|
|
2184
|
-
track("purchase_failed", {
|
|
2185
|
-
plan_identifier: selectedPlan,
|
|
2186
|
-
reason: "purchase_returned_false"
|
|
2187
|
-
});
|
|
2188
|
-
}
|
|
2189
|
-
} catch (err) {
|
|
2190
|
-
const errorMessage = err instanceof Error ? err.message : labels.purchaseError;
|
|
2191
|
-
track("purchase_failed", {
|
|
2192
|
-
plan_identifier: selectedPlan,
|
|
2193
|
-
reason: errorMessage
|
|
2194
|
-
});
|
|
2195
|
-
onError == null ? void 0 : onError(labels.errorTitle, errorMessage);
|
|
2196
|
-
} finally {
|
|
2197
|
-
setIsPurchasing(false);
|
|
2198
|
-
}
|
|
2199
|
-
}, [
|
|
2200
|
-
selectedPlan,
|
|
2201
|
-
track,
|
|
2202
|
-
onPurchase,
|
|
2203
|
-
onPurchaseSuccess,
|
|
2204
|
-
labels.errorTitle,
|
|
2205
|
-
labels.purchaseError,
|
|
2206
|
-
onError
|
|
2207
|
-
]);
|
|
2208
|
-
const handleRestore = useCallback(async () => {
|
|
2209
|
-
setIsRestoring(true);
|
|
2210
|
-
track("restore_initiated");
|
|
2211
|
-
try {
|
|
2212
|
-
const result = await onRestore();
|
|
2213
|
-
if (result) {
|
|
2214
|
-
await refreshSubscription();
|
|
2215
|
-
track("restore_completed");
|
|
2216
|
-
onRestoreSuccess == null ? void 0 : onRestoreSuccess();
|
|
2217
|
-
} else {
|
|
2218
|
-
track("restore_failed", { reason: "no_purchases_found" });
|
|
2219
|
-
onWarning == null ? void 0 : onWarning(labels.errorTitle, labels.restoreNoPurchases);
|
|
2220
|
-
}
|
|
2221
|
-
} catch (err) {
|
|
2222
|
-
const errorMessage = err instanceof Error ? err.message : labels.restoreError;
|
|
2223
|
-
track("restore_failed", { reason: errorMessage });
|
|
2224
|
-
onError == null ? void 0 : onError(labels.errorTitle, errorMessage);
|
|
2225
|
-
} finally {
|
|
2226
|
-
setIsRestoring(false);
|
|
2227
|
-
}
|
|
2228
|
-
}, [
|
|
2229
|
-
track,
|
|
2230
|
-
onRestore,
|
|
2231
|
-
onRestoreSuccess,
|
|
2232
|
-
labels.errorTitle,
|
|
2233
|
-
labels.restoreNoPurchases,
|
|
2234
|
-
labels.restoreError,
|
|
2235
|
-
onWarning,
|
|
2236
|
-
onError
|
|
2237
|
-
]);
|
|
2238
|
-
const isUpgrading = useMemo(() => {
|
|
2239
|
-
if (!(currentSubscription == null ? void 0 : currentSubscription.isActive)) return false;
|
|
2240
|
-
if (!selectedPlan) return false;
|
|
2241
|
-
return selectedPlan !== currentSubscription.packageId && selectedPlan !== currentSubscription.productId;
|
|
2242
|
-
}, [currentSubscription, selectedPlan]);
|
|
2243
|
-
const handleUpgrade = useCallback(() => {
|
|
2244
|
-
if (!(currentSubscription == null ? void 0 : currentSubscription.managementUrl)) {
|
|
2245
|
-
onError == null ? void 0 : onError(
|
|
2246
|
-
labels.errorTitle,
|
|
2247
|
-
"Unable to open subscription management. Please try again."
|
|
2248
|
-
);
|
|
2249
|
-
return;
|
|
2250
|
-
}
|
|
2251
|
-
track("upgrade_initiated", {
|
|
2252
|
-
current_plan: currentSubscription.packageId,
|
|
2253
|
-
target_plan: selectedPlan
|
|
2254
|
-
});
|
|
2255
|
-
window.open(currentSubscription.managementUrl, "_blank");
|
|
2256
|
-
}, [currentSubscription, selectedPlan, track, onError, labels.errorTitle]);
|
|
2257
|
-
const formatExpirationDate = useCallback((date) => {
|
|
2258
|
-
if (!date) return "";
|
|
2259
|
-
return new Intl.DateTimeFormat(void 0, {
|
|
2260
|
-
year: "numeric",
|
|
2261
|
-
month: "long",
|
|
2262
|
-
day: "numeric"
|
|
2263
|
-
}).format(date);
|
|
2264
|
-
}, []);
|
|
2265
|
-
const formatProductName = useCallback(
|
|
2266
|
-
(packageId, productId) => {
|
|
2267
|
-
if (!packageId && !productId) return labels.labelPremium;
|
|
2268
|
-
const pkg = packages.find(
|
|
2269
|
-
(p) => {
|
|
2270
|
-
var _a;
|
|
2271
|
-
return p.packageId === packageId || ((_a = p.product) == null ? void 0 : _a.productId) === productId;
|
|
2272
|
-
}
|
|
2273
|
-
);
|
|
2274
|
-
if (pkg == null ? void 0 : pkg.name) return pkg.name;
|
|
2275
|
-
const identifier = packageId || productId || "";
|
|
2276
|
-
return identifier.split(/[_-]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
2277
|
-
},
|
|
2278
|
-
[packages, labels.labelPremium]
|
|
2279
|
-
);
|
|
2280
|
-
const formatRateLimit = useCallback(
|
|
2281
|
-
(limit) => {
|
|
2282
|
-
if (limit === null) return labels.unlimited;
|
|
2283
|
-
return limit.toLocaleString();
|
|
2284
|
-
},
|
|
2285
|
-
[labels.unlimited]
|
|
2286
|
-
);
|
|
2287
|
-
const freeTierPackage = packages.find((p) => !p.product);
|
|
2288
|
-
const paidPackages = packages.filter((p) => p.product);
|
|
2289
|
-
return /* @__PURE__ */ jsx(
|
|
2290
|
-
SubscriptionLayout,
|
|
2291
|
-
{
|
|
2292
|
-
title: labels.title,
|
|
2293
|
-
error: error == null ? void 0 : error.message,
|
|
2294
|
-
currentStatusLabel: labels.currentStatusLabel,
|
|
2295
|
-
currentStatus: {
|
|
2296
|
-
isActive: (currentSubscription == null ? void 0 : currentSubscription.isActive) ?? false,
|
|
2297
|
-
activeContent: (currentSubscription == null ? void 0 : currentSubscription.isActive) ? {
|
|
2298
|
-
title: labels.statusActive,
|
|
2299
|
-
fields: [
|
|
2300
|
-
{
|
|
2301
|
-
label: labels.labelPlan,
|
|
2302
|
-
value: formatProductName(
|
|
2303
|
-
currentSubscription.packageId,
|
|
2304
|
-
currentSubscription.productId
|
|
2305
|
-
)
|
|
2306
|
-
},
|
|
2307
|
-
{
|
|
2308
|
-
label: labels.labelExpires,
|
|
2309
|
-
value: formatExpirationDate(
|
|
2310
|
-
currentSubscription.expirationDate
|
|
2311
|
-
)
|
|
2312
|
-
},
|
|
2313
|
-
{
|
|
2314
|
-
label: labels.labelWillRenew,
|
|
2315
|
-
value: currentSubscription.willRenew ? labels.yes : labels.no
|
|
2316
|
-
},
|
|
2317
|
-
...rateLimitsConfig ? [
|
|
2318
|
-
{
|
|
2319
|
-
label: labels.labelMonthlyUsage,
|
|
2320
|
-
value: `${rateLimitsConfig.currentUsage.monthly.toLocaleString()} / ${formatRateLimit(rateLimitsConfig.currentLimits.monthly)}`
|
|
2321
|
-
},
|
|
2322
|
-
{
|
|
2323
|
-
label: labels.labelDailyUsage,
|
|
2324
|
-
value: `${rateLimitsConfig.currentUsage.daily.toLocaleString()} / ${formatRateLimit(rateLimitsConfig.currentLimits.daily)}`
|
|
2325
|
-
}
|
|
2326
|
-
] : []
|
|
2327
|
-
]
|
|
2328
|
-
} : void 0,
|
|
2329
|
-
inactiveContent: !(currentSubscription == null ? void 0 : currentSubscription.isActive) ? {
|
|
2330
|
-
title: labels.statusInactive,
|
|
2331
|
-
message: labels.statusInactiveMessage
|
|
2332
|
-
} : void 0
|
|
2333
|
-
},
|
|
2334
|
-
aboveProducts: !isLoading && periods.length > 1 ? /* @__PURE__ */ jsx("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsx(
|
|
2335
|
-
SegmentedControl,
|
|
2336
|
-
{
|
|
2337
|
-
options: billingPeriodOptions,
|
|
2338
|
-
value: selectedPeriod,
|
|
2339
|
-
onChange: handlePeriodChange
|
|
2340
|
-
}
|
|
2341
|
-
) }) : null,
|
|
2342
|
-
primaryAction: {
|
|
2343
|
-
label: isPurchasing ? labels.buttonPurchasing : isUpgrading ? labels.buttonUpgrade : labels.buttonSubscribe,
|
|
2344
|
-
onClick: isUpgrading ? handleUpgrade : handlePurchase,
|
|
2345
|
-
disabled: !selectedPlan || isPurchasing || isRestoring,
|
|
2346
|
-
loading: isPurchasing
|
|
2347
|
-
},
|
|
2348
|
-
secondaryAction: {
|
|
2349
|
-
label: isRestoring ? labels.buttonRestoring : labels.buttonRestore,
|
|
2350
|
-
onClick: handleRestore,
|
|
2351
|
-
disabled: isPurchasing || isRestoring,
|
|
2352
|
-
loading: isRestoring
|
|
2353
|
-
},
|
|
2354
|
-
children: isLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600" }) }) : error ? /* @__PURE__ */ jsx("div", { className: "text-center py-12 text-red-500", children: error.message }) : packages.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-12 text-theme-text-secondary", children: labels.noProducts }) : paidPackages.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-12 text-theme-text-secondary", children: labels.noProductsForPeriod }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2355
|
-
freeTierPackage && !(currentSubscription == null ? void 0 : currentSubscription.isActive) && /* @__PURE__ */ jsx(
|
|
2356
|
-
SubscriptionTile,
|
|
2357
|
-
{
|
|
2358
|
-
id: "free",
|
|
2359
|
-
title: labels.freeTierTitle,
|
|
2360
|
-
price: labels.freeTierPrice,
|
|
2361
|
-
periodLabel: labels.periodMonth,
|
|
2362
|
-
features: labels.freeTierFeatures,
|
|
2363
|
-
isSelected: selectedPlan === null,
|
|
2364
|
-
isCurrentPlan: !(currentSubscription == null ? void 0 : currentSubscription.isActive),
|
|
2365
|
-
onSelect: () => handlePlanSelect(null),
|
|
2366
|
-
enabled: isPackageEnabled("free"),
|
|
2367
|
-
topBadge: {
|
|
2368
|
-
text: labels.currentPlanBadge,
|
|
2369
|
-
color: "green"
|
|
2370
|
-
},
|
|
2371
|
-
disabled: isPurchasing || isRestoring,
|
|
2372
|
-
hideSelectionIndicator: true
|
|
2373
|
-
},
|
|
2374
|
-
"free"
|
|
2375
|
-
),
|
|
2376
|
-
paidPackages.map((pkg) => {
|
|
2377
|
-
var _a, _b, _c, _d, _e, _f;
|
|
2378
|
-
const isCurrent = isCurrentPlan(
|
|
2379
|
-
pkg.packageId,
|
|
2380
|
-
(_a = pkg.product) == null ? void 0 : _a.productId
|
|
2381
|
-
);
|
|
2382
|
-
const isEnabled = isPackageEnabled(pkg.packageId);
|
|
2383
|
-
const canUpgrade = canUpgradeTo(
|
|
2384
|
-
pkg.packageId,
|
|
2385
|
-
(_b = pkg.product) == null ? void 0 : _b.productId
|
|
2386
|
-
);
|
|
2387
|
-
const canSelect = canUpgrade || isCurrent;
|
|
2388
|
-
return /* @__PURE__ */ jsx(
|
|
2389
|
-
SubscriptionTile,
|
|
2390
|
-
{
|
|
2391
|
-
id: pkg.packageId,
|
|
2392
|
-
title: pkg.name,
|
|
2393
|
-
price: ((_c = pkg.product) == null ? void 0 : _c.priceString) ?? "$0",
|
|
2394
|
-
periodLabel: getPeriodLabel(((_d = pkg.product) == null ? void 0 : _d.period) ?? "monthly"),
|
|
2395
|
-
features: formatters.getProductFeatures(pkg.packageId),
|
|
2396
|
-
isSelected: selectedPlan === pkg.packageId,
|
|
2397
|
-
isCurrentPlan: isCurrent,
|
|
2398
|
-
onSelect: () => canSelect && handlePlanSelect(pkg.packageId),
|
|
2399
|
-
enabled: isEnabled,
|
|
2400
|
-
isBestValue: pkg.packageId.includes("pro"),
|
|
2401
|
-
topBadge: isCurrent ? {
|
|
2402
|
-
text: labels.currentPlanBadge,
|
|
2403
|
-
color: "green"
|
|
2404
|
-
} : void 0,
|
|
2405
|
-
discountBadge: selectedPeriod === "yearly" ? (() => {
|
|
2406
|
-
const savings = getYearlySavingsPercent(pkg);
|
|
2407
|
-
return savings && savings > 0 ? {
|
|
2408
|
-
text: formatters.formatSavePercent(savings),
|
|
2409
|
-
isBestValue: true
|
|
2410
|
-
} : void 0;
|
|
2411
|
-
})() : void 0,
|
|
2412
|
-
introPriceNote: ((_e = pkg.product) == null ? void 0 : _e.trialPeriod) ? (() => {
|
|
2413
|
-
var _a2;
|
|
2414
|
-
const period = (_a2 = pkg.product) == null ? void 0 : _a2.trialPeriod;
|
|
2415
|
-
if (!period) return void 0;
|
|
2416
|
-
const num = parseInt(
|
|
2417
|
-
period.replace(/\D/g, "") || "1",
|
|
2418
|
-
10
|
|
2419
|
-
);
|
|
2420
|
-
if (period.includes("W"))
|
|
2421
|
-
return formatters.formatTrialWeeks(num);
|
|
2422
|
-
if (period.includes("M"))
|
|
2423
|
-
return formatters.formatTrialMonths(num);
|
|
2424
|
-
return formatters.formatTrialDays(num);
|
|
2425
|
-
})() : ((_f = pkg.product) == null ? void 0 : _f.introPrice) ? formatters.formatIntroNote(pkg.product.introPrice) : void 0,
|
|
2426
|
-
disabled: isPurchasing || isRestoring || !canSelect
|
|
2427
|
-
},
|
|
2428
|
-
pkg.packageId
|
|
2429
|
-
);
|
|
2430
|
-
})
|
|
2431
|
-
] })
|
|
2432
|
-
}
|
|
2433
|
-
);
|
|
2434
|
-
}
|
|
2435
|
-
function AppPricingPage({
|
|
2436
|
-
isAuthenticated,
|
|
2437
|
-
labels,
|
|
2438
|
-
formatters,
|
|
2439
|
-
onPlanClick,
|
|
2440
|
-
onFreePlanClick,
|
|
2441
|
-
onPurchase,
|
|
2442
|
-
onPurchaseSuccess,
|
|
2443
|
-
onPurchaseError,
|
|
2444
|
-
faqItems,
|
|
2445
|
-
className,
|
|
2446
|
-
onTrack,
|
|
2447
|
-
offerId
|
|
2448
|
-
}) {
|
|
2449
|
-
const [isPurchasing, setIsPurchasing] = useState(false);
|
|
2450
|
-
const {
|
|
2451
|
-
subscription: currentSubscription,
|
|
2452
|
-
isLoading: subscriptionLoading,
|
|
2453
|
-
error: subscriptionError,
|
|
2454
|
-
update: updateSubscription
|
|
2455
|
-
} = useUserSubscription();
|
|
2456
|
-
useEffect(() => {
|
|
2457
|
-
updateSubscription();
|
|
2458
|
-
}, [updateSubscription]);
|
|
2459
|
-
const hasActiveSubscription = (currentSubscription == null ? void 0 : currentSubscription.isActive) ?? false;
|
|
2460
|
-
const currentProductIdentifier = currentSubscription == null ? void 0 : currentSubscription.productId;
|
|
2461
|
-
const {
|
|
2462
|
-
periods,
|
|
2463
|
-
isLoading: periodsLoading,
|
|
2464
|
-
error: periodsError
|
|
2465
|
-
} = useSubscriptionPeriods(offerId);
|
|
2466
|
-
const [selectedPeriod, setSelectedPeriod] = useState("monthly");
|
|
2467
|
-
useEffect(() => {
|
|
2468
|
-
if (periods.length > 0 && !periods.includes(selectedPeriod)) {
|
|
2469
|
-
setSelectedPeriod(periods[0]);
|
|
2470
|
-
}
|
|
2471
|
-
}, [periods, selectedPeriod]);
|
|
2472
|
-
const {
|
|
2473
|
-
packages,
|
|
2474
|
-
isLoading: packagesLoading,
|
|
2475
|
-
error: packagesError
|
|
2476
|
-
} = useSubscriptionForPeriod(offerId, selectedPeriod);
|
|
2477
|
-
const {
|
|
2478
|
-
subscribablePackageIds,
|
|
2479
|
-
isLoading: subscribableLoading,
|
|
2480
|
-
error: subscribableError
|
|
2481
|
-
} = useSubscribable(offerId);
|
|
2482
|
-
const isLoading = periodsLoading || packagesLoading || subscribableLoading || subscriptionLoading || isPurchasing;
|
|
2483
|
-
const error = periodsError || packagesError || subscribableError || subscriptionError;
|
|
2484
|
-
const track = useCallback(
|
|
2485
|
-
(label, params) => {
|
|
2486
|
-
onTrack == null ? void 0 : onTrack({
|
|
2487
|
-
eventType: "subscription_action",
|
|
2488
|
-
componentName: "AppPricingPage",
|
|
2489
|
-
label,
|
|
2490
|
-
params
|
|
2491
|
-
});
|
|
2492
|
-
},
|
|
2493
|
-
[onTrack]
|
|
2494
|
-
);
|
|
2495
|
-
const handleBillingPeriodChange = useCallback(
|
|
2496
|
-
(value) => {
|
|
2497
|
-
const newPeriod = value;
|
|
2498
|
-
setSelectedPeriod(newPeriod);
|
|
2499
|
-
track("billing_period_changed", { billing_period: newPeriod });
|
|
2500
|
-
},
|
|
2501
|
-
[track]
|
|
2502
|
-
);
|
|
2503
|
-
const handleFreePlanClick = useCallback(() => {
|
|
2504
|
-
track("free_plan_clicked", { plan: "free" });
|
|
2505
|
-
onFreePlanClick();
|
|
2506
|
-
}, [track, onFreePlanClick]);
|
|
2507
|
-
const handlePlanClick = useCallback(
|
|
2508
|
-
async (planIdentifier, actionType) => {
|
|
2509
|
-
track("plan_clicked", {
|
|
2510
|
-
plan_identifier: planIdentifier,
|
|
2511
|
-
action_type: actionType
|
|
2512
|
-
});
|
|
2513
|
-
if (actionType === "login") {
|
|
2514
|
-
onPlanClick(planIdentifier);
|
|
2515
|
-
return;
|
|
2516
|
-
}
|
|
2517
|
-
if (onPurchase) {
|
|
2518
|
-
setIsPurchasing(true);
|
|
2519
|
-
try {
|
|
2520
|
-
const result = await onPurchase(planIdentifier);
|
|
2521
|
-
if (result) {
|
|
2522
|
-
await refreshSubscription();
|
|
2523
|
-
track("purchase_completed", { plan_identifier: planIdentifier });
|
|
2524
|
-
onPurchaseSuccess == null ? void 0 : onPurchaseSuccess();
|
|
2525
|
-
}
|
|
2526
|
-
} catch (err) {
|
|
2527
|
-
track("purchase_failed", {
|
|
2528
|
-
plan_identifier: planIdentifier,
|
|
2529
|
-
reason: err instanceof Error ? err.message : "Unknown error"
|
|
2530
|
-
});
|
|
2531
|
-
onPurchaseError == null ? void 0 : onPurchaseError(
|
|
2532
|
-
err instanceof Error ? err : new Error("Purchase failed")
|
|
2533
|
-
);
|
|
2534
|
-
} finally {
|
|
2535
|
-
setIsPurchasing(false);
|
|
2536
|
-
}
|
|
2537
|
-
} else {
|
|
2538
|
-
onPlanClick(planIdentifier);
|
|
2539
|
-
}
|
|
2540
|
-
},
|
|
2541
|
-
[track, onPlanClick, onPurchase, onPurchaseSuccess, onPurchaseError]
|
|
2542
|
-
);
|
|
2543
|
-
const getPeriodLabel = useCallback(
|
|
2544
|
-
(period) => {
|
|
2545
|
-
switch (period) {
|
|
2546
|
-
case "yearly":
|
|
2547
|
-
return labels.periodYear;
|
|
2548
|
-
case "monthly":
|
|
2549
|
-
return labels.periodMonth;
|
|
2550
|
-
case "weekly":
|
|
2551
|
-
return labels.periodWeek;
|
|
2552
|
-
default:
|
|
2553
|
-
return period;
|
|
2554
|
-
}
|
|
2555
|
-
},
|
|
2556
|
-
[labels]
|
|
2557
|
-
);
|
|
2558
|
-
const getYearlySavingsPercent = useCallback(
|
|
2559
|
-
(yearlyPackage) => {
|
|
2560
|
-
if (!yearlyPackage.product) return void 0;
|
|
2561
|
-
const monthlyPackageId = yearlyPackage.packageId.replace(
|
|
2562
|
-
"_yearly",
|
|
2563
|
-
"_monthly"
|
|
2564
|
-
);
|
|
2565
|
-
const monthlyPkg = packages.find((p) => p.packageId === monthlyPackageId);
|
|
2566
|
-
if (!(monthlyPkg == null ? void 0 : monthlyPkg.product)) return void 0;
|
|
2567
|
-
const yearlyPrice = yearlyPackage.product.price;
|
|
2568
|
-
const monthlyPrice = monthlyPkg.product.price;
|
|
2569
|
-
if (monthlyPrice <= 0 || yearlyPrice <= 0) return void 0;
|
|
2570
|
-
const annualizedMonthly = monthlyPrice * 12;
|
|
2571
|
-
const savings = (annualizedMonthly - yearlyPrice) / annualizedMonthly * 100;
|
|
2572
|
-
return Math.round(savings);
|
|
2573
|
-
},
|
|
2574
|
-
[packages]
|
|
2575
|
-
);
|
|
2576
|
-
const billingPeriodOptions = useMemo(() => {
|
|
2577
|
-
return periods.map((period) => ({
|
|
2578
|
-
value: period,
|
|
2579
|
-
label: period === "monthly" ? labels.billingMonthly : labels.billingYearly
|
|
2580
|
-
}));
|
|
2581
|
-
}, [periods, labels]);
|
|
2582
|
-
const isCurrentPlan = useCallback(
|
|
2583
|
-
(packageId, productId) => {
|
|
2584
|
-
if (!isAuthenticated) return false;
|
|
2585
|
-
if (!hasActiveSubscription) return false;
|
|
2586
|
-
return productId === currentProductIdentifier || packageId === currentProductIdentifier;
|
|
2587
|
-
},
|
|
2588
|
-
[isAuthenticated, hasActiveSubscription, currentProductIdentifier]
|
|
2589
|
-
);
|
|
2590
|
-
const isPackageEnabled = useCallback(
|
|
2591
|
-
(packageId) => {
|
|
2592
|
-
if (!isAuthenticated) return true;
|
|
2593
|
-
if (subscribableLoading || subscribablePackageIds.length === 0)
|
|
2594
|
-
return true;
|
|
2595
|
-
return subscribablePackageIds.includes(packageId);
|
|
2596
|
-
},
|
|
2597
|
-
[isAuthenticated, subscribableLoading, subscribablePackageIds]
|
|
2598
|
-
);
|
|
2599
|
-
const canUpgradeTo = useCallback(
|
|
2600
|
-
(packageId, productId) => {
|
|
2601
|
-
return isPackageEnabled(packageId) && !isCurrentPlan(packageId, productId);
|
|
2602
|
-
},
|
|
2603
|
-
[isPackageEnabled, isCurrentPlan]
|
|
2604
|
-
);
|
|
2605
|
-
const freeTierPackage = packages.find((p) => !p.product);
|
|
2606
|
-
const paidPackages = packages.filter((p) => p.product);
|
|
2607
|
-
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
2608
|
-
/* @__PURE__ */ jsx(Section, { spacing: "2xl", maxWidth: "4xl", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
2609
|
-
/* @__PURE__ */ jsx("h1", { className: "text-4xl sm:text-5xl font-bold text-theme-text-primary mb-4", children: labels.title }),
|
|
2610
|
-
/* @__PURE__ */ jsx("p", { className: "text-lg text-theme-text-secondary", children: labels.subtitle })
|
|
2611
|
-
] }) }),
|
|
2612
|
-
/* @__PURE__ */ jsxs(Section, { spacing: "3xl", maxWidth: "6xl", children: [
|
|
2613
|
-
periods.length > 1 && /* @__PURE__ */ jsx("div", { className: "flex justify-center mb-8", children: /* @__PURE__ */ jsx(
|
|
2614
|
-
SegmentedControl,
|
|
2615
|
-
{
|
|
2616
|
-
options: billingPeriodOptions,
|
|
2617
|
-
value: selectedPeriod,
|
|
2618
|
-
onChange: handleBillingPeriodChange
|
|
2619
|
-
}
|
|
2620
|
-
) }),
|
|
2621
|
-
isLoading && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600" }) }),
|
|
2622
|
-
error && !isLoading && /* @__PURE__ */ jsx("div", { className: "text-center py-12 text-red-500", children: error.message }),
|
|
2623
|
-
!isLoading && !error && /* @__PURE__ */ jsxs(
|
|
2624
|
-
"div",
|
|
2625
|
-
{
|
|
2626
|
-
style: {
|
|
2627
|
-
display: "grid",
|
|
2628
|
-
gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 280px), 1fr))",
|
|
2629
|
-
gridAutoRows: "1fr",
|
|
2630
|
-
gap: "1.5rem",
|
|
2631
|
-
overflow: "visible"
|
|
2632
|
-
},
|
|
2633
|
-
children: [
|
|
2634
|
-
freeTierPackage && /* @__PURE__ */ jsx(
|
|
2635
|
-
SubscriptionTile,
|
|
2636
|
-
{
|
|
2637
|
-
id: "free",
|
|
2638
|
-
title: labels.freeTierTitle,
|
|
2639
|
-
price: labels.freeTierPrice,
|
|
2640
|
-
periodLabel: labels.periodMonth,
|
|
2641
|
-
features: labels.freeTierFeatures,
|
|
2642
|
-
isSelected: false,
|
|
2643
|
-
isCurrentPlan: isAuthenticated && !hasActiveSubscription,
|
|
2644
|
-
onSelect: () => {
|
|
2645
|
-
},
|
|
2646
|
-
enabled: isPackageEnabled("free"),
|
|
2647
|
-
topBadge: isAuthenticated && !hasActiveSubscription ? {
|
|
2648
|
-
text: labels.currentPlanBadge,
|
|
2649
|
-
color: "green"
|
|
2650
|
-
} : void 0,
|
|
2651
|
-
ctaButton: (
|
|
2652
|
-
// Not logged in: show "Try it for Free"
|
|
2653
|
-
// Logged in on free plan: no CTA (current plan)
|
|
2654
|
-
// Logged in with subscription: no CTA (can't downgrade here)
|
|
2655
|
-
!isAuthenticated ? {
|
|
2656
|
-
label: labels.ctaTryFree,
|
|
2657
|
-
onClick: handleFreePlanClick
|
|
2658
|
-
} : void 0
|
|
2659
|
-
),
|
|
2660
|
-
hideSelectionIndicator: isAuthenticated
|
|
2661
|
-
}
|
|
2662
|
-
),
|
|
2663
|
-
paidPackages.map((pkg) => {
|
|
2664
|
-
var _a, _b, _c, _d;
|
|
2665
|
-
const isCurrent = isCurrentPlan(
|
|
2666
|
-
pkg.packageId,
|
|
2667
|
-
(_a = pkg.product) == null ? void 0 : _a.productId
|
|
2668
|
-
);
|
|
2669
|
-
const isEnabled = isPackageEnabled(pkg.packageId);
|
|
2670
|
-
const canUpgrade = canUpgradeTo(
|
|
2671
|
-
pkg.packageId,
|
|
2672
|
-
(_b = pkg.product) == null ? void 0 : _b.productId
|
|
2673
|
-
);
|
|
2674
|
-
let ctaButton;
|
|
2675
|
-
if (!isAuthenticated) {
|
|
2676
|
-
ctaButton = {
|
|
2677
|
-
label: labels.ctaLogIn,
|
|
2678
|
-
onClick: () => handlePlanClick(pkg.packageId, "login")
|
|
2679
|
-
};
|
|
2680
|
-
} else if (isCurrent) {
|
|
2681
|
-
ctaButton = void 0;
|
|
2682
|
-
} else if (canUpgrade) {
|
|
2683
|
-
ctaButton = {
|
|
2684
|
-
label: labels.ctaUpgrade,
|
|
2685
|
-
onClick: () => handlePlanClick(pkg.packageId, "upgrade")
|
|
2686
|
-
};
|
|
2687
|
-
}
|
|
2688
|
-
let topBadge;
|
|
2689
|
-
if (isCurrent) {
|
|
2690
|
-
topBadge = {
|
|
2691
|
-
text: labels.currentPlanBadge,
|
|
2692
|
-
color: "green"
|
|
2693
|
-
};
|
|
2694
|
-
} else if (pkg.packageId.includes("pro")) {
|
|
2695
|
-
topBadge = {
|
|
2696
|
-
text: labels.mostPopularBadge,
|
|
2697
|
-
color: "yellow"
|
|
2698
|
-
};
|
|
2699
|
-
}
|
|
2700
|
-
return /* @__PURE__ */ jsx(
|
|
2701
|
-
SubscriptionTile,
|
|
2702
|
-
{
|
|
2703
|
-
id: pkg.packageId,
|
|
2704
|
-
title: pkg.name,
|
|
2705
|
-
price: ((_c = pkg.product) == null ? void 0 : _c.priceString) ?? "$0",
|
|
2706
|
-
periodLabel: getPeriodLabel(((_d = pkg.product) == null ? void 0 : _d.period) ?? "monthly"),
|
|
2707
|
-
features: formatters.getProductFeatures(pkg.packageId),
|
|
2708
|
-
isSelected: false,
|
|
2709
|
-
isCurrentPlan: isCurrent,
|
|
2710
|
-
onSelect: () => {
|
|
2711
|
-
},
|
|
2712
|
-
enabled: isEnabled,
|
|
2713
|
-
isBestValue: pkg.packageId.includes("pro"),
|
|
2714
|
-
topBadge,
|
|
2715
|
-
discountBadge: selectedPeriod === "yearly" ? (() => {
|
|
2716
|
-
const savings = getYearlySavingsPercent(pkg);
|
|
2717
|
-
return savings && savings > 0 ? {
|
|
2718
|
-
text: formatters.formatSavePercent(savings),
|
|
2719
|
-
isBestValue: true
|
|
2720
|
-
} : void 0;
|
|
2721
|
-
})() : void 0,
|
|
2722
|
-
ctaButton,
|
|
2723
|
-
hideSelectionIndicator: !ctaButton
|
|
2724
|
-
},
|
|
2725
|
-
pkg.packageId
|
|
2726
|
-
);
|
|
2727
|
-
})
|
|
2728
|
-
]
|
|
2729
|
-
}
|
|
2730
|
-
)
|
|
2731
|
-
] }),
|
|
2732
|
-
faqItems && faqItems.length > 0 && /* @__PURE__ */ jsxs(Section, { spacing: "3xl", background: "surface", maxWidth: "3xl", children: [
|
|
2733
|
-
/* @__PURE__ */ jsx("h2", { className: "text-3xl font-bold text-theme-text-primary text-center mb-12", children: labels.faqTitle }),
|
|
2734
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-6", children: faqItems.map((item, index) => /* @__PURE__ */ jsxs(
|
|
2735
|
-
"div",
|
|
2736
|
-
{
|
|
2737
|
-
className: "bg-theme-bg-primary p-6 rounded-xl border border-theme-border",
|
|
2738
|
-
children: [
|
|
2739
|
-
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-theme-text-primary mb-2", children: item.question }),
|
|
2740
|
-
/* @__PURE__ */ jsx("p", { className: "text-theme-text-secondary", children: item.answer })
|
|
2741
|
-
]
|
|
2742
|
-
},
|
|
2743
|
-
index
|
|
2744
|
-
)) })
|
|
2745
|
-
] })
|
|
2746
|
-
] });
|
|
2747
|
-
}
|
|
2748
2024
|
const DEFAULT_SUPPORTED_LANGUAGES = ["en"];
|
|
2749
2025
|
const DEFAULT_NAMESPACES = [
|
|
2750
2026
|
"common",
|
|
@@ -2830,9 +2106,7 @@ export {
|
|
|
2830
2106
|
AppFooter,
|
|
2831
2107
|
AppFooterForHomePage,
|
|
2832
2108
|
AppPageLayout,
|
|
2833
|
-
AppPricingPage,
|
|
2834
2109
|
AppSitemapPage,
|
|
2835
|
-
AppSubscriptionsPage,
|
|
2836
2110
|
AppTextPage,
|
|
2837
2111
|
AppTopBar,
|
|
2838
2112
|
AppTopBarWithFirebaseAuth,
|