@sudobility/building_blocks 0.0.60 → 0.0.64
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/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { ChevronDownIcon, CalendarDaysIcon, PaintBrushIcon, LanguageIcon, Chevro
|
|
|
7
7
|
import { GRADIENT_CLASSES, textVariants } from "@sudobility/design";
|
|
8
8
|
import { cva } from "class-variance-authority";
|
|
9
9
|
import { SubscriptionLayout, SubscriptionTile, SegmentedControl } from "@sudobility/subscription-components";
|
|
10
|
+
import { useSubscriptionPeriods, useSubscriptionForPeriod, useSubscribable, useUserSubscription } from "@sudobility/subscription_lib";
|
|
10
11
|
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, GoogleAuthProvider, signInWithPopup } from "firebase/auth";
|
|
11
12
|
function cn(...inputs) {
|
|
12
13
|
return twMerge(clsx(inputs));
|
|
@@ -1258,212 +1259,54 @@ const GlobalSettingsPage = ({
|
|
|
1258
1259
|
}
|
|
1259
1260
|
) });
|
|
1260
1261
|
};
|
|
1261
|
-
function useSubscriptions(offerId) {
|
|
1262
|
-
const [offer, setOffer] = useState(null);
|
|
1263
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
1264
|
-
const [error, setError] = useState(null);
|
|
1265
|
-
const loadData = useCallback(async () => {
|
|
1266
|
-
{
|
|
1267
|
-
setError(new Error("Subscription not initialized"));
|
|
1268
|
-
setIsLoading(false);
|
|
1269
|
-
return;
|
|
1270
|
-
}
|
|
1271
|
-
}, [offerId]);
|
|
1272
|
-
useEffect(() => {
|
|
1273
|
-
loadData();
|
|
1274
|
-
}, [loadData]);
|
|
1275
|
-
const refetch = useCallback(async () => {
|
|
1276
|
-
return;
|
|
1277
|
-
}, [offerId]);
|
|
1278
|
-
return { offer, isLoading, error, refetch };
|
|
1279
|
-
}
|
|
1280
|
-
function useUserSubscription() {
|
|
1281
|
-
const [subscription, setSubscription] = useState(null);
|
|
1282
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
1283
|
-
const [error, setError] = useState(null);
|
|
1284
|
-
const loadData = useCallback(async () => {
|
|
1285
|
-
{
|
|
1286
|
-
setError(new Error("Subscription not initialized"));
|
|
1287
|
-
setIsLoading(false);
|
|
1288
|
-
return;
|
|
1289
|
-
}
|
|
1290
|
-
}, []);
|
|
1291
|
-
useEffect(() => {
|
|
1292
|
-
loadData();
|
|
1293
|
-
}, [loadData]);
|
|
1294
|
-
const refetch = useCallback(async () => {
|
|
1295
|
-
return;
|
|
1296
|
-
}, []);
|
|
1297
|
-
return { subscription, isLoading, error, refetch };
|
|
1298
|
-
}
|
|
1299
|
-
function calculatePackageLevels(packages) {
|
|
1300
|
-
var _a;
|
|
1301
|
-
const levels = /* @__PURE__ */ new Map();
|
|
1302
|
-
const byPeriod = /* @__PURE__ */ new Map();
|
|
1303
|
-
for (const pkg of packages) {
|
|
1304
|
-
if (!pkg.product) {
|
|
1305
|
-
levels.set(pkg.packageId, 0);
|
|
1306
|
-
continue;
|
|
1307
|
-
}
|
|
1308
|
-
const period = pkg.product.period;
|
|
1309
|
-
const existing = byPeriod.get(period) ?? [];
|
|
1310
|
-
existing.push(pkg);
|
|
1311
|
-
byPeriod.set(period, existing);
|
|
1312
|
-
}
|
|
1313
|
-
for (const [_period, periodPackages] of byPeriod) {
|
|
1314
|
-
const sorted = [...periodPackages].sort((a, b) => {
|
|
1315
|
-
var _a2, _b;
|
|
1316
|
-
const priceA = ((_a2 = a.product) == null ? void 0 : _a2.price) ?? 0;
|
|
1317
|
-
const priceB = ((_b = b.product) == null ? void 0 : _b.price) ?? 0;
|
|
1318
|
-
return priceA - priceB;
|
|
1319
|
-
});
|
|
1320
|
-
let currentLevel = 0;
|
|
1321
|
-
let lastPrice = null;
|
|
1322
|
-
for (const pkg of sorted) {
|
|
1323
|
-
const price = ((_a = pkg.product) == null ? void 0 : _a.price) ?? 0;
|
|
1324
|
-
if (lastPrice === null || price > lastPrice) {
|
|
1325
|
-
currentLevel++;
|
|
1326
|
-
lastPrice = price;
|
|
1327
|
-
}
|
|
1328
|
-
levels.set(pkg.packageId, currentLevel);
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
return levels;
|
|
1332
|
-
}
|
|
1333
|
-
function findUpgradeablePackages(current, packages) {
|
|
1334
|
-
let currentPackageId;
|
|
1335
|
-
let currentProductId;
|
|
1336
|
-
if (typeof current === "string") {
|
|
1337
|
-
currentPackageId = current;
|
|
1338
|
-
} else if (current) {
|
|
1339
|
-
currentPackageId = current.packageId;
|
|
1340
|
-
currentProductId = current.productId;
|
|
1341
|
-
}
|
|
1342
|
-
if (!currentPackageId && !currentProductId) {
|
|
1343
|
-
return packages.map((p) => p.packageId);
|
|
1344
|
-
}
|
|
1345
|
-
let currentPkg = currentPackageId ? packages.find((p) => p.packageId === currentPackageId) : void 0;
|
|
1346
|
-
if (!currentPkg && currentProductId) {
|
|
1347
|
-
currentPkg = packages.find((p) => {
|
|
1348
|
-
var _a;
|
|
1349
|
-
return ((_a = p.product) == null ? void 0 : _a.productId) === currentProductId;
|
|
1350
|
-
});
|
|
1351
|
-
}
|
|
1352
|
-
if (!currentPkg) {
|
|
1353
|
-
return packages.map((p) => p.packageId);
|
|
1354
|
-
}
|
|
1355
|
-
const matchedPackageId = currentPkg.packageId;
|
|
1356
|
-
if (!currentPkg.product) {
|
|
1357
|
-
return packages.filter((p) => p.product !== void 0).map((p) => p.packageId);
|
|
1358
|
-
}
|
|
1359
|
-
const levels = calculatePackageLevels(packages);
|
|
1360
|
-
const currentLevel = levels.get(matchedPackageId) ?? 0;
|
|
1361
|
-
const currentPeriod = currentPkg.product.period;
|
|
1362
|
-
const upgrades = [];
|
|
1363
|
-
for (const pkg of packages) {
|
|
1364
|
-
if (!pkg.product)
|
|
1365
|
-
continue;
|
|
1366
|
-
if (pkg.packageId === matchedPackageId) {
|
|
1367
|
-
continue;
|
|
1368
|
-
}
|
|
1369
|
-
const pkgLevel = levels.get(pkg.packageId) ?? 0;
|
|
1370
|
-
const pkgPeriod = pkg.product.period;
|
|
1371
|
-
const periodRanks = {
|
|
1372
|
-
weekly: 1,
|
|
1373
|
-
monthly: 2,
|
|
1374
|
-
quarterly: 3,
|
|
1375
|
-
yearly: 4,
|
|
1376
|
-
lifetime: 5
|
|
1377
|
-
};
|
|
1378
|
-
const isPeriodOk = periodRanks[pkgPeriod] >= periodRanks[currentPeriod];
|
|
1379
|
-
const isLevelOk = pkgLevel >= currentLevel;
|
|
1380
|
-
if (isPeriodOk && isLevelOk) {
|
|
1381
|
-
upgrades.push(pkg.packageId);
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
return upgrades;
|
|
1385
|
-
}
|
|
1386
|
-
function useSubscribable(offerId) {
|
|
1387
|
-
const { offer, isLoading: loadingOffer, error: offerError } = useSubscriptions(offerId);
|
|
1388
|
-
const { subscription, isLoading: loadingSub, error: subError } = useUserSubscription();
|
|
1389
|
-
const isLoading = loadingOffer || loadingSub;
|
|
1390
|
-
const error = offerError || subError;
|
|
1391
|
-
const subscribablePackageIds = useMemo(() => {
|
|
1392
|
-
if (!offer) {
|
|
1393
|
-
return [];
|
|
1394
|
-
}
|
|
1395
|
-
let allPackages = [...offer.packages];
|
|
1396
|
-
if (!subscription || !subscription.isActive) {
|
|
1397
|
-
return allPackages.map((p) => p.packageId);
|
|
1398
|
-
}
|
|
1399
|
-
const current = {
|
|
1400
|
-
packageId: subscription.packageId,
|
|
1401
|
-
productId: subscription.productId
|
|
1402
|
-
};
|
|
1403
|
-
if (!current.packageId && !current.productId) {
|
|
1404
|
-
return allPackages.map((p) => p.packageId);
|
|
1405
|
-
}
|
|
1406
|
-
return findUpgradeablePackages(current, allPackages);
|
|
1407
|
-
}, [offer, subscription]);
|
|
1408
|
-
return { subscribablePackageIds, isLoading, error };
|
|
1409
|
-
}
|
|
1410
1262
|
function AppSubscriptionsPage({
|
|
1411
|
-
|
|
1263
|
+
offerId,
|
|
1412
1264
|
rateLimitsConfig,
|
|
1413
|
-
subscriptionUserId,
|
|
1414
1265
|
labels,
|
|
1415
1266
|
formatters,
|
|
1267
|
+
onPurchase,
|
|
1268
|
+
onRestore,
|
|
1416
1269
|
onPurchaseSuccess,
|
|
1417
1270
|
onRestoreSuccess,
|
|
1418
1271
|
onError,
|
|
1419
1272
|
onWarning,
|
|
1420
|
-
onTrack
|
|
1421
|
-
offerId
|
|
1273
|
+
onTrack
|
|
1422
1274
|
}) {
|
|
1423
|
-
const { subscribablePackageIds } = useSubscribable(offerId);
|
|
1424
1275
|
const {
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1276
|
+
periods,
|
|
1277
|
+
isLoading: periodsLoading,
|
|
1278
|
+
error: periodsError
|
|
1279
|
+
} = useSubscriptionPeriods(offerId);
|
|
1280
|
+
const [selectedPeriod, setSelectedPeriod] = useState("monthly");
|
|
1281
|
+
useEffect(() => {
|
|
1282
|
+
if (periods.length > 0 && !periods.includes(selectedPeriod)) {
|
|
1283
|
+
setSelectedPeriod(periods[0]);
|
|
1284
|
+
}
|
|
1285
|
+
}, [periods, selectedPeriod]);
|
|
1286
|
+
const {
|
|
1287
|
+
packages,
|
|
1288
|
+
isLoading: packagesLoading,
|
|
1289
|
+
error: packagesError
|
|
1290
|
+
} = useSubscriptionForPeriod(offerId, selectedPeriod);
|
|
1291
|
+
const {
|
|
1292
|
+
subscribablePackageIds,
|
|
1293
|
+
isLoading: subscribableLoading,
|
|
1294
|
+
error: subscribableError
|
|
1295
|
+
} = useSubscribable(offerId);
|
|
1296
|
+
const {
|
|
1297
|
+
subscription: currentSubscription,
|
|
1298
|
+
isLoading: subscriptionLoading,
|
|
1299
|
+
error: subscriptionError,
|
|
1300
|
+
update: updateSubscription
|
|
1301
|
+
} = useUserSubscription();
|
|
1302
|
+
useEffect(() => {
|
|
1303
|
+
updateSubscription();
|
|
1304
|
+
}, [updateSubscription]);
|
|
1305
|
+
const isLoading = periodsLoading || packagesLoading || subscribableLoading || subscriptionLoading;
|
|
1306
|
+
const error = periodsError || packagesError || subscribableError || subscriptionError;
|
|
1434
1307
|
const [selectedPlan, setSelectedPlan] = useState(null);
|
|
1435
1308
|
const [isPurchasing, setIsPurchasing] = useState(false);
|
|
1436
1309
|
const [isRestoring, setIsRestoring] = useState(false);
|
|
1437
|
-
const isCurrentPlanProduct = useCallback(
|
|
1438
|
-
(productId) => {
|
|
1439
|
-
return !!((currentSubscription == null ? void 0 : currentSubscription.isActive) && currentSubscription.productIdentifier === productId);
|
|
1440
|
-
},
|
|
1441
|
-
[currentSubscription]
|
|
1442
|
-
);
|
|
1443
|
-
const isPackageEnabled = useCallback(
|
|
1444
|
-
(productId) => {
|
|
1445
|
-
return subscribablePackageIds.includes(productId);
|
|
1446
|
-
},
|
|
1447
|
-
[subscribablePackageIds]
|
|
1448
|
-
);
|
|
1449
|
-
const canUpgradeTo = useCallback(
|
|
1450
|
-
(productId) => {
|
|
1451
|
-
return isPackageEnabled(productId) && !isCurrentPlanProduct(productId);
|
|
1452
|
-
},
|
|
1453
|
-
[isPackageEnabled, isCurrentPlanProduct]
|
|
1454
|
-
);
|
|
1455
|
-
useEffect(() => {
|
|
1456
|
-
if ((currentSubscription == null ? void 0 : currentSubscription.isActive) && currentSubscription.productIdentifier) {
|
|
1457
|
-
setSelectedPlan(currentSubscription.productIdentifier);
|
|
1458
|
-
const currentProduct = products.find(
|
|
1459
|
-
(p) => p.identifier === currentSubscription.productIdentifier
|
|
1460
|
-
);
|
|
1461
|
-
if (currentProduct == null ? void 0 : currentProduct.period) {
|
|
1462
|
-
const isYearly = currentProduct.period.includes("Y") || currentProduct.period.includes("year");
|
|
1463
|
-
setBillingPeriod(isYearly ? "yearly" : "monthly");
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
}, [currentSubscription, products]);
|
|
1467
1310
|
const track = useCallback(
|
|
1468
1311
|
(label, params) => {
|
|
1469
1312
|
onTrack == null ? void 0 : onTrack({
|
|
@@ -1475,32 +1318,99 @@ function AppSubscriptionsPage({
|
|
|
1475
1318
|
},
|
|
1476
1319
|
[onTrack]
|
|
1477
1320
|
);
|
|
1478
|
-
useEffect(() => {
|
|
1479
|
-
if (error) {
|
|
1480
|
-
onError == null ? void 0 : onError(labels.errorTitle, error);
|
|
1481
|
-
clearError();
|
|
1482
|
-
}
|
|
1483
|
-
}, [error, clearError, labels.errorTitle, onError]);
|
|
1484
|
-
const filteredProducts = products.filter((product) => {
|
|
1485
|
-
if (!product.period) return false;
|
|
1486
|
-
const isYearly = product.period.includes("Y") || product.period.includes("year");
|
|
1487
|
-
return billingPeriod === "yearly" ? isYearly : !isYearly;
|
|
1488
|
-
}).sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
|
|
1489
1321
|
const handlePeriodChange = useCallback(
|
|
1490
|
-
(
|
|
1491
|
-
|
|
1322
|
+
(value) => {
|
|
1323
|
+
const newPeriod = value;
|
|
1324
|
+
setSelectedPeriod(newPeriod);
|
|
1492
1325
|
setSelectedPlan(null);
|
|
1493
|
-
track("billing_period_changed", { billing_period:
|
|
1326
|
+
track("billing_period_changed", { billing_period: newPeriod });
|
|
1327
|
+
},
|
|
1328
|
+
[track]
|
|
1329
|
+
);
|
|
1330
|
+
const getPeriodLabel = useCallback(
|
|
1331
|
+
(period) => {
|
|
1332
|
+
switch (period) {
|
|
1333
|
+
case "yearly":
|
|
1334
|
+
return labels.periodYear;
|
|
1335
|
+
case "monthly":
|
|
1336
|
+
return labels.periodMonth;
|
|
1337
|
+
case "weekly":
|
|
1338
|
+
return labels.periodWeek;
|
|
1339
|
+
default:
|
|
1340
|
+
return period;
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1343
|
+
[labels]
|
|
1344
|
+
);
|
|
1345
|
+
const getYearlySavingsPercent = useCallback(
|
|
1346
|
+
(yearlyPackage) => {
|
|
1347
|
+
if (!yearlyPackage.product) return void 0;
|
|
1348
|
+
const monthlyPackageId = yearlyPackage.packageId.replace(
|
|
1349
|
+
"_yearly",
|
|
1350
|
+
"_monthly"
|
|
1351
|
+
);
|
|
1352
|
+
const monthlyPkg = packages.find((p) => p.packageId === monthlyPackageId);
|
|
1353
|
+
if (!(monthlyPkg == null ? void 0 : monthlyPkg.product)) return void 0;
|
|
1354
|
+
const yearlyPrice = yearlyPackage.product.price;
|
|
1355
|
+
const monthlyPrice = monthlyPkg.product.price;
|
|
1356
|
+
if (monthlyPrice <= 0 || yearlyPrice <= 0) return void 0;
|
|
1357
|
+
const annualizedMonthly = monthlyPrice * 12;
|
|
1358
|
+
const savings = (annualizedMonthly - yearlyPrice) / annualizedMonthly * 100;
|
|
1359
|
+
return Math.round(savings);
|
|
1360
|
+
},
|
|
1361
|
+
[packages]
|
|
1362
|
+
);
|
|
1363
|
+
const billingPeriodOptions = useMemo(() => {
|
|
1364
|
+
return periods.map((period) => ({
|
|
1365
|
+
value: period,
|
|
1366
|
+
label: period === "monthly" ? labels.billingMonthly : labels.billingYearly
|
|
1367
|
+
}));
|
|
1368
|
+
}, [periods, labels]);
|
|
1369
|
+
const isCurrentPlan = useCallback(
|
|
1370
|
+
(packageId, productId) => {
|
|
1371
|
+
if (!(currentSubscription == null ? void 0 : currentSubscription.isActive)) return false;
|
|
1372
|
+
return productId === currentSubscription.productId || packageId === currentSubscription.packageId;
|
|
1373
|
+
},
|
|
1374
|
+
[currentSubscription]
|
|
1375
|
+
);
|
|
1376
|
+
const isPackageEnabled = useCallback(
|
|
1377
|
+
(packageId) => {
|
|
1378
|
+
if (subscribableLoading || subscribablePackageIds.length === 0)
|
|
1379
|
+
return true;
|
|
1380
|
+
return subscribablePackageIds.includes(packageId);
|
|
1381
|
+
},
|
|
1382
|
+
[subscribableLoading, subscribablePackageIds]
|
|
1383
|
+
);
|
|
1384
|
+
const canUpgradeTo = useCallback(
|
|
1385
|
+
(packageId, productId) => {
|
|
1386
|
+
return isPackageEnabled(packageId) && !isCurrentPlan(packageId, productId);
|
|
1387
|
+
},
|
|
1388
|
+
[isPackageEnabled, isCurrentPlan]
|
|
1389
|
+
);
|
|
1390
|
+
useEffect(() => {
|
|
1391
|
+
if ((currentSubscription == null ? void 0 : currentSubscription.isActive) && currentSubscription.packageId) {
|
|
1392
|
+
setSelectedPlan(currentSubscription.packageId);
|
|
1393
|
+
if (currentSubscription.period && periods.includes(currentSubscription.period)) {
|
|
1394
|
+
setSelectedPeriod(currentSubscription.period);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
}, [currentSubscription, periods]);
|
|
1398
|
+
const handlePlanSelect = useCallback(
|
|
1399
|
+
(planIdentifier) => {
|
|
1400
|
+
setSelectedPlan(planIdentifier);
|
|
1401
|
+
track("plan_selected", {
|
|
1402
|
+
plan_identifier: planIdentifier ?? "free",
|
|
1403
|
+
is_free_tier: planIdentifier === null
|
|
1404
|
+
});
|
|
1494
1405
|
},
|
|
1495
1406
|
[track]
|
|
1496
1407
|
);
|
|
1497
1408
|
const handlePurchase = useCallback(async () => {
|
|
1498
1409
|
if (!selectedPlan) return;
|
|
1499
1410
|
setIsPurchasing(true);
|
|
1500
|
-
clearError();
|
|
1501
1411
|
track("purchase_initiated", { plan_identifier: selectedPlan });
|
|
1502
1412
|
try {
|
|
1503
|
-
const result = await
|
|
1413
|
+
const result = await onPurchase(selectedPlan);
|
|
1504
1414
|
if (result) {
|
|
1505
1415
|
track("purchase_completed", { plan_identifier: selectedPlan });
|
|
1506
1416
|
onPurchaseSuccess == null ? void 0 : onPurchaseSuccess();
|
|
@@ -1523,31 +1433,18 @@ function AppSubscriptionsPage({
|
|
|
1523
1433
|
}
|
|
1524
1434
|
}, [
|
|
1525
1435
|
selectedPlan,
|
|
1526
|
-
clearError,
|
|
1527
1436
|
track,
|
|
1528
|
-
|
|
1529
|
-
subscriptionUserId,
|
|
1437
|
+
onPurchase,
|
|
1530
1438
|
onPurchaseSuccess,
|
|
1531
1439
|
labels.errorTitle,
|
|
1532
1440
|
labels.purchaseError,
|
|
1533
1441
|
onError
|
|
1534
1442
|
]);
|
|
1535
|
-
const handlePlanSelect = useCallback(
|
|
1536
|
-
(planIdentifier) => {
|
|
1537
|
-
setSelectedPlan(planIdentifier);
|
|
1538
|
-
track("plan_selected", {
|
|
1539
|
-
plan_identifier: planIdentifier ?? "free",
|
|
1540
|
-
is_free_tier: planIdentifier === null
|
|
1541
|
-
});
|
|
1542
|
-
},
|
|
1543
|
-
[track]
|
|
1544
|
-
);
|
|
1545
1443
|
const handleRestore = useCallback(async () => {
|
|
1546
1444
|
setIsRestoring(true);
|
|
1547
|
-
clearError();
|
|
1548
1445
|
track("restore_initiated");
|
|
1549
1446
|
try {
|
|
1550
|
-
const result = await
|
|
1447
|
+
const result = await onRestore();
|
|
1551
1448
|
if (result) {
|
|
1552
1449
|
track("restore_completed");
|
|
1553
1450
|
onRestoreSuccess == null ? void 0 : onRestoreSuccess();
|
|
@@ -1563,10 +1460,8 @@ function AppSubscriptionsPage({
|
|
|
1563
1460
|
setIsRestoring(false);
|
|
1564
1461
|
}
|
|
1565
1462
|
}, [
|
|
1566
|
-
clearError,
|
|
1567
1463
|
track,
|
|
1568
|
-
|
|
1569
|
-
subscriptionUserId,
|
|
1464
|
+
onRestore,
|
|
1570
1465
|
onRestoreSuccess,
|
|
1571
1466
|
labels.errorTitle,
|
|
1572
1467
|
labels.restoreNoPurchases,
|
|
@@ -1582,41 +1477,20 @@ function AppSubscriptionsPage({
|
|
|
1582
1477
|
day: "numeric"
|
|
1583
1478
|
}).format(date);
|
|
1584
1479
|
}, []);
|
|
1585
|
-
const
|
|
1586
|
-
(
|
|
1587
|
-
if (!
|
|
1588
|
-
const
|
|
1589
|
-
|
|
1480
|
+
const formatProductName = useCallback(
|
|
1481
|
+
(packageId, productId) => {
|
|
1482
|
+
if (!packageId && !productId) return labels.labelPremium;
|
|
1483
|
+
const pkg = packages.find(
|
|
1484
|
+
(p) => {
|
|
1485
|
+
var _a;
|
|
1486
|
+
return p.packageId === packageId || ((_a = p.product) == null ? void 0 : _a.productId) === productId;
|
|
1487
|
+
}
|
|
1488
|
+
);
|
|
1489
|
+
if (pkg == null ? void 0 : pkg.name) return pkg.name;
|
|
1490
|
+
const identifier = packageId || productId || "";
|
|
1590
1491
|
return identifier.split(/[_-]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
1591
1492
|
},
|
|
1592
|
-
[
|
|
1593
|
-
);
|
|
1594
|
-
const getPeriodLabel = useCallback(
|
|
1595
|
-
(period) => {
|
|
1596
|
-
if (!period) return "";
|
|
1597
|
-
if (period.includes("Y") || period.includes("year"))
|
|
1598
|
-
return labels.periodYear;
|
|
1599
|
-
if (period.includes("M") || period.includes("month"))
|
|
1600
|
-
return labels.periodMonth;
|
|
1601
|
-
if (period.includes("W") || period.includes("week"))
|
|
1602
|
-
return labels.periodWeek;
|
|
1603
|
-
return "";
|
|
1604
|
-
},
|
|
1605
|
-
[labels]
|
|
1606
|
-
);
|
|
1607
|
-
const getTrialLabel = useCallback(
|
|
1608
|
-
(trialPeriod) => {
|
|
1609
|
-
if (!trialPeriod) return void 0;
|
|
1610
|
-
const num = parseInt(trialPeriod.replace(/\D/g, "") || "1", 10);
|
|
1611
|
-
if (trialPeriod.includes("W")) {
|
|
1612
|
-
return formatters.formatTrialWeeks(num);
|
|
1613
|
-
}
|
|
1614
|
-
if (trialPeriod.includes("M")) {
|
|
1615
|
-
return formatters.formatTrialMonths(num);
|
|
1616
|
-
}
|
|
1617
|
-
return formatters.formatTrialDays(num);
|
|
1618
|
-
},
|
|
1619
|
-
[formatters]
|
|
1493
|
+
[packages, labels.labelPremium]
|
|
1620
1494
|
);
|
|
1621
1495
|
const formatRateLimit = useCallback(
|
|
1622
1496
|
(limit) => {
|
|
@@ -1625,44 +1499,13 @@ function AppSubscriptionsPage({
|
|
|
1625
1499
|
},
|
|
1626
1500
|
[labels.unlimited]
|
|
1627
1501
|
);
|
|
1628
|
-
const
|
|
1629
|
-
|
|
1630
|
-
return formatters.getProductFeatures(packageId);
|
|
1631
|
-
},
|
|
1632
|
-
[formatters]
|
|
1633
|
-
);
|
|
1634
|
-
const getFreeTierFeatures = useCallback(() => {
|
|
1635
|
-
return labels.freeTierFeatures;
|
|
1636
|
-
}, [labels.freeTierFeatures]);
|
|
1637
|
-
const getYearlySavingsPercent = useCallback(
|
|
1638
|
-
(yearlyPackageId) => {
|
|
1639
|
-
const yearlyProduct = products.find(
|
|
1640
|
-
(p) => p.identifier === yearlyPackageId
|
|
1641
|
-
);
|
|
1642
|
-
if (!yearlyProduct) return void 0;
|
|
1643
|
-
const monthlyPackageId = yearlyPackageId.replace("_yearly", "_monthly");
|
|
1644
|
-
const monthlyProduct = products.find(
|
|
1645
|
-
(p) => p.identifier === monthlyPackageId
|
|
1646
|
-
);
|
|
1647
|
-
if (!monthlyProduct) return void 0;
|
|
1648
|
-
const yearlyPrice = parseFloat(yearlyProduct.price);
|
|
1649
|
-
const monthlyPrice = parseFloat(monthlyProduct.price);
|
|
1650
|
-
if (monthlyPrice <= 0 || yearlyPrice <= 0) return void 0;
|
|
1651
|
-
const annualizedMonthly = monthlyPrice * 12;
|
|
1652
|
-
const savings = (annualizedMonthly - yearlyPrice) / annualizedMonthly * 100;
|
|
1653
|
-
return Math.round(savings);
|
|
1654
|
-
},
|
|
1655
|
-
[products]
|
|
1656
|
-
);
|
|
1657
|
-
const billingPeriodOptions = [
|
|
1658
|
-
{ value: "monthly", label: labels.billingMonthly },
|
|
1659
|
-
{ value: "yearly", label: labels.billingYearly }
|
|
1660
|
-
];
|
|
1502
|
+
const freeTierPackage = packages.find((p) => !p.product);
|
|
1503
|
+
const paidPackages = packages.filter((p) => p.product);
|
|
1661
1504
|
return /* @__PURE__ */ jsx(
|
|
1662
1505
|
SubscriptionLayout,
|
|
1663
1506
|
{
|
|
1664
1507
|
title: labels.title,
|
|
1665
|
-
error,
|
|
1508
|
+
error: error == null ? void 0 : error.message,
|
|
1666
1509
|
currentStatusLabel: labels.currentStatusLabel,
|
|
1667
1510
|
currentStatus: {
|
|
1668
1511
|
isActive: (currentSubscription == null ? void 0 : currentSubscription.isActive) ?? false,
|
|
@@ -1671,8 +1514,9 @@ function AppSubscriptionsPage({
|
|
|
1671
1514
|
fields: [
|
|
1672
1515
|
{
|
|
1673
1516
|
label: labels.labelPlan,
|
|
1674
|
-
value:
|
|
1675
|
-
currentSubscription.
|
|
1517
|
+
value: formatProductName(
|
|
1518
|
+
currentSubscription.packageId,
|
|
1519
|
+
currentSubscription.productId
|
|
1676
1520
|
)
|
|
1677
1521
|
},
|
|
1678
1522
|
{
|
|
@@ -1702,11 +1546,11 @@ function AppSubscriptionsPage({
|
|
|
1702
1546
|
message: labels.statusInactiveMessage
|
|
1703
1547
|
} : void 0
|
|
1704
1548
|
},
|
|
1705
|
-
aboveProducts: !isLoading &&
|
|
1549
|
+
aboveProducts: !isLoading && periods.length > 1 ? /* @__PURE__ */ jsx("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsx(
|
|
1706
1550
|
SegmentedControl,
|
|
1707
1551
|
{
|
|
1708
1552
|
options: billingPeriodOptions,
|
|
1709
|
-
value:
|
|
1553
|
+
value: selectedPeriod,
|
|
1710
1554
|
onChange: handlePeriodChange
|
|
1711
1555
|
}
|
|
1712
1556
|
) }) : null,
|
|
@@ -1722,19 +1566,19 @@ function AppSubscriptionsPage({
|
|
|
1722
1566
|
disabled: isPurchasing || isRestoring,
|
|
1723
1567
|
loading: isRestoring
|
|
1724
1568
|
},
|
|
1725
|
-
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" }) }) :
|
|
1726
|
-
!(currentSubscription == null ? void 0 : currentSubscription.isActive) && /* @__PURE__ */ jsx(
|
|
1569
|
+
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: [
|
|
1570
|
+
freeTierPackage && !(currentSubscription == null ? void 0 : currentSubscription.isActive) && /* @__PURE__ */ jsx(
|
|
1727
1571
|
SubscriptionTile,
|
|
1728
1572
|
{
|
|
1729
1573
|
id: "free",
|
|
1730
1574
|
title: labels.freeTierTitle,
|
|
1731
1575
|
price: labels.freeTierPrice,
|
|
1732
1576
|
periodLabel: labels.periodMonth,
|
|
1733
|
-
features:
|
|
1577
|
+
features: labels.freeTierFeatures,
|
|
1734
1578
|
isSelected: selectedPlan === null,
|
|
1735
1579
|
isCurrentPlan: !(currentSubscription == null ? void 0 : currentSubscription.isActive),
|
|
1736
1580
|
onSelect: () => handlePlanSelect(null),
|
|
1737
|
-
enabled:
|
|
1581
|
+
enabled: isPackageEnabled("free"),
|
|
1738
1582
|
topBadge: {
|
|
1739
1583
|
text: labels.currentPlanBadge,
|
|
1740
1584
|
color: "green"
|
|
@@ -1744,42 +1588,59 @@ function AppSubscriptionsPage({
|
|
|
1744
1588
|
},
|
|
1745
1589
|
"free"
|
|
1746
1590
|
),
|
|
1747
|
-
|
|
1748
|
-
var _a;
|
|
1749
|
-
const isCurrent =
|
|
1750
|
-
|
|
1751
|
-
|
|
1591
|
+
paidPackages.map((pkg) => {
|
|
1592
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1593
|
+
const isCurrent = isCurrentPlan(
|
|
1594
|
+
pkg.packageId,
|
|
1595
|
+
(_a = pkg.product) == null ? void 0 : _a.productId
|
|
1596
|
+
);
|
|
1597
|
+
const isEnabled = isPackageEnabled(pkg.packageId);
|
|
1598
|
+
const canUpgrade = canUpgradeTo(
|
|
1599
|
+
pkg.packageId,
|
|
1600
|
+
(_b = pkg.product) == null ? void 0 : _b.productId
|
|
1601
|
+
);
|
|
1752
1602
|
const canSelect = canUpgrade || isCurrent;
|
|
1753
1603
|
return /* @__PURE__ */ jsx(
|
|
1754
1604
|
SubscriptionTile,
|
|
1755
1605
|
{
|
|
1756
|
-
id:
|
|
1757
|
-
title:
|
|
1758
|
-
price: product.priceString,
|
|
1759
|
-
periodLabel: getPeriodLabel(product.period),
|
|
1760
|
-
features: getProductFeatures(
|
|
1761
|
-
isSelected: selectedPlan ===
|
|
1606
|
+
id: pkg.packageId,
|
|
1607
|
+
title: pkg.name,
|
|
1608
|
+
price: ((_c = pkg.product) == null ? void 0 : _c.priceString) ?? "$0",
|
|
1609
|
+
periodLabel: getPeriodLabel(((_d = pkg.product) == null ? void 0 : _d.period) ?? "monthly"),
|
|
1610
|
+
features: formatters.getProductFeatures(pkg.packageId),
|
|
1611
|
+
isSelected: selectedPlan === pkg.packageId,
|
|
1762
1612
|
isCurrentPlan: isCurrent,
|
|
1763
|
-
onSelect: () => canSelect && handlePlanSelect(
|
|
1613
|
+
onSelect: () => canSelect && handlePlanSelect(pkg.packageId),
|
|
1764
1614
|
enabled: isEnabled,
|
|
1765
|
-
isBestValue:
|
|
1615
|
+
isBestValue: pkg.packageId.includes("pro"),
|
|
1766
1616
|
topBadge: isCurrent ? {
|
|
1767
1617
|
text: labels.currentPlanBadge,
|
|
1768
1618
|
color: "green"
|
|
1769
1619
|
} : void 0,
|
|
1770
|
-
discountBadge:
|
|
1771
|
-
const savings = getYearlySavingsPercent(
|
|
1772
|
-
product.identifier
|
|
1773
|
-
);
|
|
1620
|
+
discountBadge: selectedPeriod === "yearly" ? (() => {
|
|
1621
|
+
const savings = getYearlySavingsPercent(pkg);
|
|
1774
1622
|
return savings && savings > 0 ? {
|
|
1775
1623
|
text: formatters.formatSavePercent(savings),
|
|
1776
1624
|
isBestValue: true
|
|
1777
1625
|
} : void 0;
|
|
1778
1626
|
})() : void 0,
|
|
1779
|
-
introPriceNote:
|
|
1627
|
+
introPriceNote: ((_e = pkg.product) == null ? void 0 : _e.trialPeriod) ? (() => {
|
|
1628
|
+
var _a2;
|
|
1629
|
+
const period = (_a2 = pkg.product) == null ? void 0 : _a2.trialPeriod;
|
|
1630
|
+
if (!period) return void 0;
|
|
1631
|
+
const num = parseInt(
|
|
1632
|
+
period.replace(/\D/g, "") || "1",
|
|
1633
|
+
10
|
|
1634
|
+
);
|
|
1635
|
+
if (period.includes("W"))
|
|
1636
|
+
return formatters.formatTrialWeeks(num);
|
|
1637
|
+
if (period.includes("M"))
|
|
1638
|
+
return formatters.formatTrialMonths(num);
|
|
1639
|
+
return formatters.formatTrialDays(num);
|
|
1640
|
+
})() : ((_f = pkg.product) == null ? void 0 : _f.introPrice) ? formatters.formatIntroNote(pkg.product.introPrice) : void 0,
|
|
1780
1641
|
disabled: isPurchasing || isRestoring || !canSelect
|
|
1781
1642
|
},
|
|
1782
|
-
|
|
1643
|
+
pkg.packageId
|
|
1783
1644
|
);
|
|
1784
1645
|
})
|
|
1785
1646
|
] })
|
|
@@ -1787,10 +1648,7 @@ function AppSubscriptionsPage({
|
|
|
1787
1648
|
);
|
|
1788
1649
|
}
|
|
1789
1650
|
function AppPricingPage({
|
|
1790
|
-
products,
|
|
1791
1651
|
isAuthenticated,
|
|
1792
|
-
hasActiveSubscription,
|
|
1793
|
-
currentProductIdentifier,
|
|
1794
1652
|
labels,
|
|
1795
1653
|
formatters,
|
|
1796
1654
|
onPlanClick,
|
|
@@ -1800,8 +1658,40 @@ function AppPricingPage({
|
|
|
1800
1658
|
onTrack,
|
|
1801
1659
|
offerId
|
|
1802
1660
|
}) {
|
|
1803
|
-
const
|
|
1804
|
-
|
|
1661
|
+
const {
|
|
1662
|
+
subscription: currentSubscription,
|
|
1663
|
+
isLoading: subscriptionLoading,
|
|
1664
|
+
error: subscriptionError,
|
|
1665
|
+
update: updateSubscription
|
|
1666
|
+
} = useUserSubscription();
|
|
1667
|
+
useEffect(() => {
|
|
1668
|
+
updateSubscription();
|
|
1669
|
+
}, [updateSubscription]);
|
|
1670
|
+
const hasActiveSubscription = (currentSubscription == null ? void 0 : currentSubscription.isActive) ?? false;
|
|
1671
|
+
const currentProductIdentifier = currentSubscription == null ? void 0 : currentSubscription.productId;
|
|
1672
|
+
const {
|
|
1673
|
+
periods,
|
|
1674
|
+
isLoading: periodsLoading,
|
|
1675
|
+
error: periodsError
|
|
1676
|
+
} = useSubscriptionPeriods(offerId);
|
|
1677
|
+
const [selectedPeriod, setSelectedPeriod] = useState("monthly");
|
|
1678
|
+
useEffect(() => {
|
|
1679
|
+
if (periods.length > 0 && !periods.includes(selectedPeriod)) {
|
|
1680
|
+
setSelectedPeriod(periods[0]);
|
|
1681
|
+
}
|
|
1682
|
+
}, [periods, selectedPeriod]);
|
|
1683
|
+
const {
|
|
1684
|
+
packages,
|
|
1685
|
+
isLoading: packagesLoading,
|
|
1686
|
+
error: packagesError
|
|
1687
|
+
} = useSubscriptionForPeriod(offerId, selectedPeriod);
|
|
1688
|
+
const {
|
|
1689
|
+
subscribablePackageIds,
|
|
1690
|
+
isLoading: subscribableLoading,
|
|
1691
|
+
error: subscribableError
|
|
1692
|
+
} = useSubscribable(offerId);
|
|
1693
|
+
const isLoading = periodsLoading || packagesLoading || subscribableLoading || subscriptionLoading;
|
|
1694
|
+
const error = periodsError || packagesError || subscribableError || subscriptionError;
|
|
1805
1695
|
const track = useCallback(
|
|
1806
1696
|
(label, params) => {
|
|
1807
1697
|
onTrack == null ? void 0 : onTrack({
|
|
@@ -1816,7 +1706,7 @@ function AppPricingPage({
|
|
|
1816
1706
|
const handleBillingPeriodChange = useCallback(
|
|
1817
1707
|
(value) => {
|
|
1818
1708
|
const newPeriod = value;
|
|
1819
|
-
|
|
1709
|
+
setSelectedPeriod(newPeriod);
|
|
1820
1710
|
track("billing_period_changed", { billing_period: newPeriod });
|
|
1821
1711
|
},
|
|
1822
1712
|
[track]
|
|
@@ -1835,84 +1725,87 @@ function AppPricingPage({
|
|
|
1835
1725
|
},
|
|
1836
1726
|
[track, onPlanClick]
|
|
1837
1727
|
);
|
|
1838
|
-
const filteredProducts = products.filter((product) => {
|
|
1839
|
-
if (!product.period) return false;
|
|
1840
|
-
const isYearly = product.period.includes("Y") || product.period.includes("year");
|
|
1841
|
-
return billingPeriod === "yearly" ? isYearly : !isYearly;
|
|
1842
|
-
}).sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
|
|
1843
1728
|
const getPeriodLabel = useCallback(
|
|
1844
1729
|
(period) => {
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1730
|
+
switch (period) {
|
|
1731
|
+
case "yearly":
|
|
1732
|
+
return labels.periodYear;
|
|
1733
|
+
case "monthly":
|
|
1734
|
+
return labels.periodMonth;
|
|
1735
|
+
case "weekly":
|
|
1736
|
+
return labels.periodWeek;
|
|
1737
|
+
default:
|
|
1738
|
+
return period;
|
|
1739
|
+
}
|
|
1853
1740
|
},
|
|
1854
1741
|
[labels]
|
|
1855
1742
|
);
|
|
1856
1743
|
const getYearlySavingsPercent = useCallback(
|
|
1857
|
-
(
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
const monthlyPackageId = yearlyPackageId.replace("_yearly", "_monthly");
|
|
1863
|
-
const monthlyProduct = products.find(
|
|
1864
|
-
(p) => p.identifier === monthlyPackageId
|
|
1744
|
+
(yearlyPackage) => {
|
|
1745
|
+
if (!yearlyPackage.product) return void 0;
|
|
1746
|
+
const monthlyPackageId = yearlyPackage.packageId.replace(
|
|
1747
|
+
"_yearly",
|
|
1748
|
+
"_monthly"
|
|
1865
1749
|
);
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
const
|
|
1750
|
+
const monthlyPkg = packages.find((p) => p.packageId === monthlyPackageId);
|
|
1751
|
+
if (!(monthlyPkg == null ? void 0 : monthlyPkg.product)) return void 0;
|
|
1752
|
+
const yearlyPrice = yearlyPackage.product.price;
|
|
1753
|
+
const monthlyPrice = monthlyPkg.product.price;
|
|
1869
1754
|
if (monthlyPrice <= 0 || yearlyPrice <= 0) return void 0;
|
|
1870
1755
|
const annualizedMonthly = monthlyPrice * 12;
|
|
1871
1756
|
const savings = (annualizedMonthly - yearlyPrice) / annualizedMonthly * 100;
|
|
1872
1757
|
return Math.round(savings);
|
|
1873
1758
|
},
|
|
1874
|
-
[
|
|
1759
|
+
[packages]
|
|
1875
1760
|
);
|
|
1876
|
-
const billingPeriodOptions =
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1761
|
+
const billingPeriodOptions = useMemo(() => {
|
|
1762
|
+
return periods.map((period) => ({
|
|
1763
|
+
value: period,
|
|
1764
|
+
label: period === "monthly" ? labels.billingMonthly : labels.billingYearly
|
|
1765
|
+
}));
|
|
1766
|
+
}, [periods, labels]);
|
|
1880
1767
|
const isCurrentPlan = useCallback(
|
|
1881
|
-
(productId) => {
|
|
1768
|
+
(packageId, productId) => {
|
|
1882
1769
|
if (!isAuthenticated) return false;
|
|
1883
1770
|
if (!hasActiveSubscription) return false;
|
|
1884
|
-
return productId === currentProductIdentifier;
|
|
1771
|
+
return productId === currentProductIdentifier || packageId === currentProductIdentifier;
|
|
1885
1772
|
},
|
|
1886
1773
|
[isAuthenticated, hasActiveSubscription, currentProductIdentifier]
|
|
1887
1774
|
);
|
|
1888
1775
|
const isPackageEnabled = useCallback(
|
|
1889
|
-
(
|
|
1776
|
+
(packageId) => {
|
|
1890
1777
|
if (!isAuthenticated) return true;
|
|
1891
|
-
|
|
1778
|
+
if (subscribableLoading || subscribablePackageIds.length === 0)
|
|
1779
|
+
return true;
|
|
1780
|
+
return subscribablePackageIds.includes(packageId);
|
|
1892
1781
|
},
|
|
1893
|
-
[isAuthenticated, subscribablePackageIds]
|
|
1782
|
+
[isAuthenticated, subscribableLoading, subscribablePackageIds]
|
|
1894
1783
|
);
|
|
1895
1784
|
const canUpgradeTo = useCallback(
|
|
1896
|
-
(productId) => {
|
|
1897
|
-
return isPackageEnabled(
|
|
1785
|
+
(packageId, productId) => {
|
|
1786
|
+
return isPackageEnabled(packageId) && !isCurrentPlan(packageId, productId);
|
|
1898
1787
|
},
|
|
1899
1788
|
[isPackageEnabled, isCurrentPlan]
|
|
1900
1789
|
);
|
|
1790
|
+
const freeTierPackage = packages.find((p) => !p.product);
|
|
1791
|
+
const paidPackages = packages.filter((p) => p.product);
|
|
1901
1792
|
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
1902
1793
|
/* @__PURE__ */ jsx(Section, { spacing: "2xl", maxWidth: "4xl", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
1903
1794
|
/* @__PURE__ */ jsx("h1", { className: "text-4xl sm:text-5xl font-bold text-theme-text-primary mb-4", children: labels.title }),
|
|
1904
1795
|
/* @__PURE__ */ jsx("p", { className: "text-lg text-theme-text-secondary", children: labels.subtitle })
|
|
1905
1796
|
] }) }),
|
|
1906
1797
|
/* @__PURE__ */ jsxs(Section, { spacing: "3xl", maxWidth: "6xl", children: [
|
|
1907
|
-
/* @__PURE__ */ jsx("div", { className: "flex justify-center mb-8", children: /* @__PURE__ */ jsx(
|
|
1798
|
+
periods.length > 1 && /* @__PURE__ */ jsx("div", { className: "flex justify-center mb-8", children: /* @__PURE__ */ jsx(
|
|
1908
1799
|
SegmentedControl,
|
|
1909
1800
|
{
|
|
1910
1801
|
options: billingPeriodOptions,
|
|
1911
|
-
value:
|
|
1802
|
+
value: selectedPeriod,
|
|
1912
1803
|
onChange: handleBillingPeriodChange
|
|
1913
1804
|
}
|
|
1914
1805
|
) }),
|
|
1915
|
-
/* @__PURE__ */
|
|
1806
|
+
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" }) }),
|
|
1807
|
+
error && !isLoading && /* @__PURE__ */ jsx("div", { className: "text-center py-12 text-red-500", children: error.message }),
|
|
1808
|
+
!isLoading && !error && /* @__PURE__ */ jsxs(
|
|
1916
1809
|
"div",
|
|
1917
1810
|
{
|
|
1918
1811
|
style: {
|
|
@@ -1923,7 +1816,7 @@ function AppPricingPage({
|
|
|
1923
1816
|
overflow: "visible"
|
|
1924
1817
|
},
|
|
1925
1818
|
children: [
|
|
1926
|
-
/* @__PURE__ */ jsx(
|
|
1819
|
+
freeTierPackage && /* @__PURE__ */ jsx(
|
|
1927
1820
|
SubscriptionTile,
|
|
1928
1821
|
{
|
|
1929
1822
|
id: "free",
|
|
@@ -1935,10 +1828,7 @@ function AppPricingPage({
|
|
|
1935
1828
|
isCurrentPlan: isAuthenticated && !hasActiveSubscription,
|
|
1936
1829
|
onSelect: () => {
|
|
1937
1830
|
},
|
|
1938
|
-
enabled: (
|
|
1939
|
-
// Free tier enabled: not authenticated, or in subscribable list
|
|
1940
|
-
!isAuthenticated || subscribablePackageIds.includes("free")
|
|
1941
|
-
),
|
|
1831
|
+
enabled: isPackageEnabled("free"),
|
|
1942
1832
|
topBadge: isAuthenticated && !hasActiveSubscription ? {
|
|
1943
1833
|
text: labels.currentPlanBadge,
|
|
1944
1834
|
color: "green"
|
|
@@ -1955,23 +1845,29 @@ function AppPricingPage({
|
|
|
1955
1845
|
hideSelectionIndicator: isAuthenticated
|
|
1956
1846
|
}
|
|
1957
1847
|
),
|
|
1958
|
-
|
|
1959
|
-
var _a;
|
|
1960
|
-
const isCurrent = isCurrentPlan(
|
|
1961
|
-
|
|
1962
|
-
|
|
1848
|
+
paidPackages.map((pkg) => {
|
|
1849
|
+
var _a, _b, _c, _d;
|
|
1850
|
+
const isCurrent = isCurrentPlan(
|
|
1851
|
+
pkg.packageId,
|
|
1852
|
+
(_a = pkg.product) == null ? void 0 : _a.productId
|
|
1853
|
+
);
|
|
1854
|
+
const isEnabled = isPackageEnabled(pkg.packageId);
|
|
1855
|
+
const canUpgrade = canUpgradeTo(
|
|
1856
|
+
pkg.packageId,
|
|
1857
|
+
(_b = pkg.product) == null ? void 0 : _b.productId
|
|
1858
|
+
);
|
|
1963
1859
|
let ctaButton;
|
|
1964
1860
|
if (!isAuthenticated) {
|
|
1965
1861
|
ctaButton = {
|
|
1966
1862
|
label: labels.ctaLogIn,
|
|
1967
|
-
onClick: () => handlePlanClick(
|
|
1863
|
+
onClick: () => handlePlanClick(pkg.packageId, "login")
|
|
1968
1864
|
};
|
|
1969
1865
|
} else if (isCurrent) {
|
|
1970
1866
|
ctaButton = void 0;
|
|
1971
1867
|
} else if (canUpgrade) {
|
|
1972
1868
|
ctaButton = {
|
|
1973
1869
|
label: labels.ctaUpgrade,
|
|
1974
|
-
onClick: () => handlePlanClick(
|
|
1870
|
+
onClick: () => handlePlanClick(pkg.packageId, "upgrade")
|
|
1975
1871
|
};
|
|
1976
1872
|
}
|
|
1977
1873
|
let topBadge;
|
|
@@ -1980,7 +1876,7 @@ function AppPricingPage({
|
|
|
1980
1876
|
text: labels.currentPlanBadge,
|
|
1981
1877
|
color: "green"
|
|
1982
1878
|
};
|
|
1983
|
-
} else if (
|
|
1879
|
+
} else if (pkg.packageId.includes("pro")) {
|
|
1984
1880
|
topBadge = {
|
|
1985
1881
|
text: labels.mostPopularBadge,
|
|
1986
1882
|
color: "yellow"
|
|
@@ -1989,22 +1885,20 @@ function AppPricingPage({
|
|
|
1989
1885
|
return /* @__PURE__ */ jsx(
|
|
1990
1886
|
SubscriptionTile,
|
|
1991
1887
|
{
|
|
1992
|
-
id:
|
|
1993
|
-
title:
|
|
1994
|
-
price: product.priceString,
|
|
1995
|
-
periodLabel: getPeriodLabel(product.period),
|
|
1996
|
-
features: formatters.getProductFeatures(
|
|
1888
|
+
id: pkg.packageId,
|
|
1889
|
+
title: pkg.name,
|
|
1890
|
+
price: ((_c = pkg.product) == null ? void 0 : _c.priceString) ?? "$0",
|
|
1891
|
+
periodLabel: getPeriodLabel(((_d = pkg.product) == null ? void 0 : _d.period) ?? "monthly"),
|
|
1892
|
+
features: formatters.getProductFeatures(pkg.packageId),
|
|
1997
1893
|
isSelected: false,
|
|
1998
1894
|
isCurrentPlan: isCurrent,
|
|
1999
1895
|
onSelect: () => {
|
|
2000
1896
|
},
|
|
2001
1897
|
enabled: isEnabled,
|
|
2002
|
-
isBestValue:
|
|
1898
|
+
isBestValue: pkg.packageId.includes("pro"),
|
|
2003
1899
|
topBadge,
|
|
2004
|
-
discountBadge:
|
|
2005
|
-
const savings = getYearlySavingsPercent(
|
|
2006
|
-
product.identifier
|
|
2007
|
-
);
|
|
1900
|
+
discountBadge: selectedPeriod === "yearly" ? (() => {
|
|
1901
|
+
const savings = getYearlySavingsPercent(pkg);
|
|
2008
1902
|
return savings && savings > 0 ? {
|
|
2009
1903
|
text: formatters.formatSavePercent(savings),
|
|
2010
1904
|
isBestValue: true
|
|
@@ -2013,7 +1907,7 @@ function AppPricingPage({
|
|
|
2013
1907
|
ctaButton,
|
|
2014
1908
|
hideSelectionIndicator: !ctaButton
|
|
2015
1909
|
},
|
|
2016
|
-
|
|
1910
|
+
pkg.packageId
|
|
2017
1911
|
);
|
|
2018
1912
|
})
|
|
2019
1913
|
]
|