@zuplo/zudoku-plugin-monetization 0.0.44 → 0.0.45
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.mjs +232 -51
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { D as formatDurationAdjective, E as formatDuration, O as formatDurationInterval, T as formatPrice, _ as formatPlanPrice, a as planHasDefaultTaxBehavior, b as sameEntitlementSet, c as getPlanPriceSchedule, d as PlanEntitlements, f as PlanPhaseHeader, l as PlanPriceTag, o as subscriptionTaxLegendSentence, p as EntitlementList, r as isCustomPlan, t as PricingTable, u as PlanPriceSchedule, w as formatMinorCurrencyAmount, x as categorizeRateCards, y as comparePlanEntitlements } from "./PricingTable-WkG2n7V-.mjs";
|
|
2
2
|
import { cn, createPlugin, joinUrl, throwIfProblemJson } from "zudoku";
|
|
3
|
-
import { AlertTriangleIcon, ArrowDownIcon, ArrowLeftRightIcon, ArrowUpIcon, CalendarIcon, CheckCheckIcon, CheckIcon, ChevronDownIcon, CircleSlashIcon, ClockIcon, CreditCardIcon, Grid2x2XIcon, InfoIcon, Loader2Icon, LockIcon, MoreVerticalIcon, RefreshCcw, RefreshCwIcon, Settings, ShieldIcon, StarsIcon, Trash2Icon, XIcon } from "zudoku/icons";
|
|
3
|
+
import { AlertTriangleIcon, ArrowDownIcon, ArrowLeftRightIcon, ArrowUpIcon, BadgePercentIcon, CalendarIcon, CheckCheckIcon, CheckIcon, ChevronDownIcon, CircleSlashIcon, ClockIcon, CreditCardIcon, Grid2x2XIcon, HistoryIcon, InfoIcon, Loader2Icon, LockIcon, MoreVerticalIcon, RefreshCcw, RefreshCwIcon, Settings, ShieldIcon, StarsIcon, Trash2Icon, XIcon } from "zudoku/icons";
|
|
4
4
|
import { Link, Outlet, useLocation, useNavigate, useSearchParams } from "zudoku/router";
|
|
5
5
|
import { useAuth, useZudoku } from "zudoku/hooks";
|
|
6
6
|
import { QueryClient, QueryClientProvider, queryOptions, useMutation, useQuery, useQueryClient, useSuspenseQuery } from "zudoku/react-query";
|
|
@@ -1034,6 +1034,24 @@ const useSubscriptions = () => {
|
|
|
1034
1034
|
});
|
|
1035
1035
|
};
|
|
1036
1036
|
//#endregion
|
|
1037
|
+
//#region src/hooks/usePendingCredits.ts
|
|
1038
|
+
/**
|
|
1039
|
+
* Fetch the operator-applied usage credits for a subscription, via the Zuplo
|
|
1040
|
+
* metering `.../pending-credits` endpoint. Failures are swallowed (`retry: false`,
|
|
1041
|
+
* `throwOnError: false`) so the usage page never breaks when credits are
|
|
1042
|
+
* unavailable — the credit banner simply isn't shown.
|
|
1043
|
+
*/
|
|
1044
|
+
const usePendingCredits = (deploymentName, subscriptionId) => {
|
|
1045
|
+
const zudoku = useZudoku();
|
|
1046
|
+
return useQuery({
|
|
1047
|
+
queryKey: [`/v3/zudoku-metering/${deploymentName}/subscriptions/${subscriptionId}/pending-credits`],
|
|
1048
|
+
meta: { context: zudoku },
|
|
1049
|
+
refetchOnWindowFocus: true,
|
|
1050
|
+
retry: false,
|
|
1051
|
+
throwOnError: false
|
|
1052
|
+
});
|
|
1053
|
+
};
|
|
1054
|
+
//#endregion
|
|
1037
1055
|
//#region src/pages/subscriptions/ConfirmDeleteKeyAlert.tsx
|
|
1038
1056
|
const ConfirmDeleteKeyAlert = ({ children, onDelete }) => {
|
|
1039
1057
|
return /* @__PURE__ */ jsxs(AlertDialog, { children: [/* @__PURE__ */ jsx(AlertDialogTrigger, {
|
|
@@ -2142,6 +2160,7 @@ const sectionLabelClassName = "text-base font-semibold tracking-wide mb-3 mt-2";
|
|
|
2142
2160
|
const formatDateTimeRange = (from, to) => `${formatDateTime(from)} – ${formatDateTime(to)}`;
|
|
2143
2161
|
const SubscriptionPlanDetails = ({ subscription }) => {
|
|
2144
2162
|
const { pricing } = useMonetizationConfig();
|
|
2163
|
+
const hasEnded = !!subscription.activeTo && new Date(subscription.activeTo).getTime() < Date.now();
|
|
2145
2164
|
const plan = subscription.plan;
|
|
2146
2165
|
const view = getSubscriptionPlanView(subscription, { units: pricing?.units });
|
|
2147
2166
|
const { priceLabel } = view;
|
|
@@ -2195,10 +2214,10 @@ const SubscriptionPlanDetails = ({ subscription }) => {
|
|
|
2195
2214
|
}) : null] })] }),
|
|
2196
2215
|
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", {
|
|
2197
2216
|
className: detailLabelClassName,
|
|
2198
|
-
children: "Current period"
|
|
2217
|
+
children: hasEnded ? "Ended" : "Current period"
|
|
2199
2218
|
}), /* @__PURE__ */ jsx("dd", {
|
|
2200
2219
|
className: "text-foreground",
|
|
2201
|
-
children: subscription.alignment?.currentAlignedBillingPeriod ? formatDateTimeRange(subscription.alignment.currentAlignedBillingPeriod.from, subscription.alignment.currentAlignedBillingPeriod.to) : "—"
|
|
2220
|
+
children: hasEnded ? formatDateTime(subscription.activeTo ?? "") : subscription.alignment?.currentAlignedBillingPeriod ? formatDateTimeRange(subscription.alignment.currentAlignedBillingPeriod.from, subscription.alignment.currentAlignedBillingPeriod.to) : "—"
|
|
2202
2221
|
})] })
|
|
2203
2222
|
]
|
|
2204
2223
|
}),
|
|
@@ -2236,48 +2255,185 @@ const SubscriptionPlanDetails = ({ subscription }) => {
|
|
|
2236
2255
|
});
|
|
2237
2256
|
};
|
|
2238
2257
|
//#endregion
|
|
2258
|
+
//#region src/utils/priceIncludedUnits.ts
|
|
2259
|
+
const partAmount = (part) => {
|
|
2260
|
+
if (!part || part.amount === void 0) return 0;
|
|
2261
|
+
const amount = parseFloat(part.amount);
|
|
2262
|
+
return Number.isFinite(amount) ? amount : Number.POSITIVE_INFINITY;
|
|
2263
|
+
};
|
|
2264
|
+
const priceIncludedUnits = (price) => {
|
|
2265
|
+
if (!price) return 0;
|
|
2266
|
+
if (price.type === "flat" || price.type === "package" || price.type === "dynamic") return 0;
|
|
2267
|
+
if (price.tiers?.length) {
|
|
2268
|
+
if ((price.mode ?? "graduated") !== "graduated") return 0;
|
|
2269
|
+
const sorted = [...price.tiers].sort((a, b) => {
|
|
2270
|
+
if (a.upToAmount === void 0) return 1;
|
|
2271
|
+
if (b.upToAmount === void 0) return -1;
|
|
2272
|
+
return Number(a.upToAmount) - Number(b.upToAmount);
|
|
2273
|
+
});
|
|
2274
|
+
let free = 0;
|
|
2275
|
+
for (const [index, tier] of sorted.entries()) {
|
|
2276
|
+
if (partAmount(tier.unitPrice) > 0) break;
|
|
2277
|
+
if (index > 0 && partAmount(tier.flatPrice) > 0) break;
|
|
2278
|
+
if (tier.upToAmount === void 0) return Number.POSITIVE_INFINITY;
|
|
2279
|
+
const bound = Number(tier.upToAmount);
|
|
2280
|
+
if (!Number.isFinite(bound) || bound <= free) break;
|
|
2281
|
+
free = bound;
|
|
2282
|
+
}
|
|
2283
|
+
return free;
|
|
2284
|
+
}
|
|
2285
|
+
if (price.type === "unit") {
|
|
2286
|
+
const amount = parseFloat(price.amount ?? "");
|
|
2287
|
+
return Number.isFinite(amount) && amount === 0 ? Number.POSITIVE_INFINITY : 0;
|
|
2288
|
+
}
|
|
2289
|
+
return 0;
|
|
2290
|
+
};
|
|
2291
|
+
//#endregion
|
|
2292
|
+
//#region src/pages/subscriptions/deriveUsageView.ts
|
|
2293
|
+
const formatAmount = (amount) => {
|
|
2294
|
+
const value = parseFloat(amount ?? "");
|
|
2295
|
+
return Number.isFinite(value) ? `$${value.toFixed(2)}` : void 0;
|
|
2296
|
+
};
|
|
2297
|
+
const pluralizeUnit = (unitName) => unitName.endsWith("s") ? unitName : `${unitName}s`;
|
|
2298
|
+
/** The label for what additional usage costs, when the shape has one number. */
|
|
2299
|
+
const rateLabelFor = (price, unitName) => {
|
|
2300
|
+
if (!price) return void 0;
|
|
2301
|
+
if (price.type === "unit") {
|
|
2302
|
+
const amount = formatAmount(price.amount);
|
|
2303
|
+
return amount ? `${amount}/${unitName}` : void 0;
|
|
2304
|
+
}
|
|
2305
|
+
if (price.type === "tiered") {
|
|
2306
|
+
const amount = formatAmount((price.tiers?.find((t) => !t.upToAmount) ?? price.tiers?.at(-1))?.unitPrice?.amount);
|
|
2307
|
+
return amount ? `${amount}/${unitName}` : void 0;
|
|
2308
|
+
}
|
|
2309
|
+
if (price.type === "package") {
|
|
2310
|
+
const amount = formatAmount(price.amount);
|
|
2311
|
+
if (!amount) return void 0;
|
|
2312
|
+
const size = parseFloat(price.quantityPerUnit ?? "");
|
|
2313
|
+
return Number.isFinite(size) && size > 0 ? `${amount} per ${size.toLocaleString()} ${pluralizeUnit(unitName)}` : amount;
|
|
2314
|
+
}
|
|
2315
|
+
};
|
|
2316
|
+
const NO_CAP = "There is no usage cap.";
|
|
2317
|
+
const deriveUsageView = (meter, item, unitName = "unit") => {
|
|
2318
|
+
const quota = meter.balance + meter.usage - meter.overage;
|
|
2319
|
+
const isSoftLimit = item?.included?.entitlement?.isSoftLimit ?? true;
|
|
2320
|
+
const rateLabel = rateLabelFor(item?.price, unitName);
|
|
2321
|
+
if (!isSoftLimit) return {
|
|
2322
|
+
kind: "capped",
|
|
2323
|
+
usage: meter.usage,
|
|
2324
|
+
quota,
|
|
2325
|
+
remaining: meter.balance,
|
|
2326
|
+
atLimit: meter.usage >= quota,
|
|
2327
|
+
rateLabel
|
|
2328
|
+
};
|
|
2329
|
+
if (!item) return {
|
|
2330
|
+
kind: "meteredGeneric",
|
|
2331
|
+
usage: meter.usage,
|
|
2332
|
+
quota: quota > 0 ? quota : void 0,
|
|
2333
|
+
caption: "Usage is billed per your plan's pricing."
|
|
2334
|
+
};
|
|
2335
|
+
if (!item.price || item.price.type === "flat") return {
|
|
2336
|
+
kind: "meteredGeneric",
|
|
2337
|
+
usage: meter.usage,
|
|
2338
|
+
quota: quota > 0 ? quota : void 0,
|
|
2339
|
+
caption: `Usage doesn't change your bill. ${NO_CAP}`
|
|
2340
|
+
};
|
|
2341
|
+
const isGraduated = item.price.type === "tiered" && (item.price.mode ?? "graduated") === "graduated";
|
|
2342
|
+
const derivable = item.price.type === "unit" || isGraduated;
|
|
2343
|
+
const freeUnits = priceIncludedUnits(item.price);
|
|
2344
|
+
if (derivable) {
|
|
2345
|
+
if (freeUnits === 0) return {
|
|
2346
|
+
kind: "payAsYouGo",
|
|
2347
|
+
usage: meter.usage,
|
|
2348
|
+
caption: `Pay as you go — every ${unitName} is billed; there is no usage cap.`,
|
|
2349
|
+
rateLabel
|
|
2350
|
+
};
|
|
2351
|
+
if (freeUnits === Number.POSITIVE_INFINITY) return {
|
|
2352
|
+
kind: "meteredGeneric",
|
|
2353
|
+
usage: meter.usage,
|
|
2354
|
+
quota: quota > 0 ? quota : void 0,
|
|
2355
|
+
caption: `Included with your plan. ${NO_CAP}`,
|
|
2356
|
+
rateLabel
|
|
2357
|
+
};
|
|
2358
|
+
if (quota > 0) return {
|
|
2359
|
+
kind: "included",
|
|
2360
|
+
usage: meter.usage,
|
|
2361
|
+
included: quota,
|
|
2362
|
+
remaining: meter.balance,
|
|
2363
|
+
overage: meter.overage,
|
|
2364
|
+
rateLabel
|
|
2365
|
+
};
|
|
2366
|
+
return {
|
|
2367
|
+
kind: "meteredGeneric",
|
|
2368
|
+
usage: meter.usage,
|
|
2369
|
+
caption: `The first ${freeUnits.toLocaleString()} ${pluralizeUnit(unitName)} are included; additional usage is billed. ${NO_CAP}`,
|
|
2370
|
+
rateLabel
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
return {
|
|
2374
|
+
kind: "meteredGeneric",
|
|
2375
|
+
usage: meter.usage,
|
|
2376
|
+
quota: quota > 0 ? quota : void 0,
|
|
2377
|
+
caption: `Usage is billed per your plan's pricing. ${NO_CAP}`,
|
|
2378
|
+
rateLabel
|
|
2379
|
+
};
|
|
2380
|
+
};
|
|
2381
|
+
//#endregion
|
|
2239
2382
|
//#region src/pages/subscriptions/Usage.tsx
|
|
2240
2383
|
const isMeteredEntitlement = (entitlement) => {
|
|
2241
2384
|
return "balance" in entitlement;
|
|
2242
2385
|
};
|
|
2243
|
-
const UsageItem = ({ meter, item, subscription, featureKey }) => {
|
|
2386
|
+
const UsageItem = ({ meter, item, subscription, featureKey, pendingCredit }) => {
|
|
2387
|
+
const { pricing } = useMonetizationConfig();
|
|
2244
2388
|
const cadence = item?.billingCadence ?? subscription?.billingCadence;
|
|
2245
2389
|
const billingPeriod = cadence ? formatDurationAdjective(cadence) : "monthly";
|
|
2246
|
-
const
|
|
2247
|
-
const
|
|
2248
|
-
const
|
|
2249
|
-
const
|
|
2250
|
-
|
|
2251
|
-
|
|
2390
|
+
const view = deriveUsageView(meter, item, pricing?.units?.[item?.key ?? ""] ?? pricing?.units?.[featureKey] ?? "unit");
|
|
2391
|
+
const atHardLimit = view.kind === "capped" && view.atLimit;
|
|
2392
|
+
const overIncluded = view.kind === "included" && view.overage > 0;
|
|
2393
|
+
const upgradeAction = (variant) => subscription && /* @__PURE__ */ jsx(AlertAction, { children: /* @__PURE__ */ jsx(SwitchPlanModal, {
|
|
2394
|
+
subscription,
|
|
2395
|
+
children: /* @__PURE__ */ jsxs(Button, {
|
|
2396
|
+
variant,
|
|
2397
|
+
size: "xs",
|
|
2398
|
+
children: [/* @__PURE__ */ jsx(ArrowUpIcon, {}), "Upgrade"]
|
|
2399
|
+
})
|
|
2400
|
+
}) });
|
|
2252
2401
|
return /* @__PURE__ */ jsxs(Card, {
|
|
2253
|
-
className: cn(
|
|
2402
|
+
className: cn(atHardLimit && "border-destructive bg-destructive/5"),
|
|
2254
2403
|
children: [/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
2255
|
-
|
|
2256
|
-
variant: "destructive",
|
|
2404
|
+
pendingCredit && /* @__PURE__ */ jsxs(Alert, {
|
|
2257
2405
|
className: "mb-4",
|
|
2258
2406
|
children: [
|
|
2259
|
-
/* @__PURE__ */ jsx(
|
|
2407
|
+
/* @__PURE__ */ jsx(BadgePercentIcon, { className: "size-4 text-green-600 shrink-0" }),
|
|
2408
|
+
/* @__PURE__ */ jsx(AlertTitle, { children: "Usage credit applied" }),
|
|
2409
|
+
/* @__PURE__ */ jsxs(AlertDescription, { children: [
|
|
2410
|
+
"A credit of ",
|
|
2411
|
+
pendingCredit.units.toLocaleString(),
|
|
2412
|
+
" ",
|
|
2413
|
+
pendingCredit.units === 1 ? "unit" : "units",
|
|
2414
|
+
" applies to this billing period and will be deducted from your next invoice automatically."
|
|
2415
|
+
] })
|
|
2416
|
+
]
|
|
2417
|
+
}),
|
|
2418
|
+
overIncluded && /* @__PURE__ */ jsxs(Alert, {
|
|
2419
|
+
variant: "warning",
|
|
2420
|
+
className: "mb-4",
|
|
2421
|
+
children: [
|
|
2422
|
+
/* @__PURE__ */ jsx(AlertTriangleIcon, { className: "size-4 shrink-0" }),
|
|
2260
2423
|
/* @__PURE__ */ jsxs(AlertTitle, { children: [
|
|
2261
|
-
"You've
|
|
2424
|
+
"You've used your included ",
|
|
2262
2425
|
billingPeriod,
|
|
2263
|
-
"
|
|
2426
|
+
" usage"
|
|
2264
2427
|
] }),
|
|
2265
2428
|
/* @__PURE__ */ jsxs(AlertDescription, { children: [
|
|
2266
|
-
"Additional usage is
|
|
2267
|
-
|
|
2268
|
-
". Upgrade to a higher plan for more usage."
|
|
2429
|
+
"Additional usage is billed",
|
|
2430
|
+
view.rateLabel ? ` at ${view.rateLabel}` : "",
|
|
2431
|
+
". Upgrade to a higher plan for more included usage."
|
|
2269
2432
|
] }),
|
|
2270
|
-
|
|
2271
|
-
subscription,
|
|
2272
|
-
children: /* @__PURE__ */ jsxs(Button, {
|
|
2273
|
-
variant: "destructive",
|
|
2274
|
-
size: "xs",
|
|
2275
|
-
children: [/* @__PURE__ */ jsx(ArrowUpIcon, {}), "Upgrade"]
|
|
2276
|
-
})
|
|
2277
|
-
}) })
|
|
2433
|
+
upgradeAction("outline")
|
|
2278
2434
|
]
|
|
2279
2435
|
}),
|
|
2280
|
-
|
|
2436
|
+
atHardLimit && /* @__PURE__ */ jsxs(Alert, {
|
|
2281
2437
|
variant: "destructive",
|
|
2282
2438
|
className: "mb-4",
|
|
2283
2439
|
children: [
|
|
@@ -2288,58 +2444,79 @@ const UsageItem = ({ meter, item, subscription, featureKey }) => {
|
|
|
2288
2444
|
" limit"
|
|
2289
2445
|
] }),
|
|
2290
2446
|
/* @__PURE__ */ jsx(AlertDescription, { children: "Requests beyond your quota are blocked. Upgrade to a higher plan for more usage." }),
|
|
2291
|
-
|
|
2292
|
-
subscription,
|
|
2293
|
-
children: /* @__PURE__ */ jsxs(Button, {
|
|
2294
|
-
variant: "destructive",
|
|
2295
|
-
size: "xs",
|
|
2296
|
-
children: [/* @__PURE__ */ jsx(ArrowUpIcon, {}), "Upgrade"]
|
|
2297
|
-
})
|
|
2298
|
-
}) })
|
|
2447
|
+
upgradeAction("destructive")
|
|
2299
2448
|
]
|
|
2300
2449
|
}),
|
|
2301
2450
|
/* @__PURE__ */ jsx(CardTitle, { children: item?.name ?? featureKey })
|
|
2302
|
-
] }), /* @__PURE__ */
|
|
2451
|
+
] }), /* @__PURE__ */ jsx(CardContent, {
|
|
2303
2452
|
className: "space-y-2",
|
|
2304
|
-
children: [
|
|
2453
|
+
children: view.kind === "capped" || view.kind === "included" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2305
2454
|
/* @__PURE__ */ jsxs("div", {
|
|
2306
2455
|
className: "flex items-center justify-between text-sm",
|
|
2307
2456
|
children: [/* @__PURE__ */ jsx("div", {
|
|
2308
2457
|
className: "flex flex-col gap-2 mb-2",
|
|
2309
2458
|
children: /* @__PURE__ */ jsxs("span", {
|
|
2310
|
-
className: cn(
|
|
2459
|
+
className: cn(atHardLimit && "text-red-600 font-medium"),
|
|
2311
2460
|
children: [
|
|
2312
|
-
|
|
2461
|
+
view.usage.toLocaleString(),
|
|
2313
2462
|
" used",
|
|
2314
|
-
|
|
2463
|
+
view.kind === "included" && view.overage > 0 && /* @__PURE__ */ jsxs("span", {
|
|
2315
2464
|
className: "ml-1 text-xs",
|
|
2316
2465
|
children: [
|
|
2317
2466
|
"(+",
|
|
2318
|
-
|
|
2467
|
+
view.overage.toLocaleString(),
|
|
2319
2468
|
" overage)"
|
|
2320
2469
|
]
|
|
2321
2470
|
})
|
|
2322
2471
|
]
|
|
2323
2472
|
})
|
|
2324
|
-
}), /* @__PURE__ */
|
|
2473
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2325
2474
|
className: "text-foreground font-medium",
|
|
2326
|
-
children:
|
|
2475
|
+
children: view.kind === "capped" ? `${view.quota.toLocaleString()} limit` : `${view.included.toLocaleString()} included`
|
|
2327
2476
|
})]
|
|
2328
2477
|
}),
|
|
2329
2478
|
/* @__PURE__ */ jsx(Progress, {
|
|
2330
|
-
value: Math.min(100,
|
|
2331
|
-
className: cn("mb-3 h-2",
|
|
2479
|
+
value: Math.min(100, (view.kind === "capped" ? view.quota : view.included) > 0 ? view.usage / (view.kind === "capped" ? view.quota : view.included) * 100 : 100),
|
|
2480
|
+
className: cn("mb-3 h-2", atHardLimit && "bg-destructive")
|
|
2332
2481
|
}),
|
|
2333
2482
|
/* @__PURE__ */ jsxs("p", {
|
|
2334
2483
|
className: "text-xs text-muted-foreground",
|
|
2335
|
-
children: [
|
|
2484
|
+
children: [
|
|
2485
|
+
view.remaining.toLocaleString(),
|
|
2486
|
+
view.kind === "included" ? " included" : "",
|
|
2487
|
+
" remaining this billing period"
|
|
2488
|
+
]
|
|
2336
2489
|
})
|
|
2337
|
-
]
|
|
2490
|
+
] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
2491
|
+
className: "flex items-center justify-between text-sm",
|
|
2492
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [view.usage.toLocaleString(), " used this billing period"] }), view.kind === "meteredGeneric" && view.quota !== void 0 ? /* @__PURE__ */ jsxs("span", {
|
|
2493
|
+
className: "text-foreground font-medium",
|
|
2494
|
+
children: [view.quota.toLocaleString(), " quota"]
|
|
2495
|
+
}) : view.rateLabel && /* @__PURE__ */ jsx("span", {
|
|
2496
|
+
className: "text-muted-foreground",
|
|
2497
|
+
children: view.rateLabel
|
|
2498
|
+
})]
|
|
2499
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2500
|
+
className: "text-xs text-muted-foreground",
|
|
2501
|
+
children: view.caption
|
|
2502
|
+
})] })
|
|
2338
2503
|
})]
|
|
2339
2504
|
});
|
|
2340
2505
|
};
|
|
2341
|
-
const Usage = ({ usage, isFetching, currentItems, subscription, isPendingFirstPayment }) => {
|
|
2506
|
+
const Usage = ({ usage, isFetching, currentItems, subscription, isPendingFirstPayment, pendingCredits }) => {
|
|
2342
2507
|
const hasUsage = Object.values(usage.entitlements).some((value) => isMeteredEntitlement(value));
|
|
2508
|
+
if (!!subscription?.activeTo && new Date(subscription.activeTo).getTime() < Date.now()) return /* @__PURE__ */ jsxs("div", {
|
|
2509
|
+
className: "space-y-4",
|
|
2510
|
+
children: [/* @__PURE__ */ jsx(Heading, {
|
|
2511
|
+
level: 3,
|
|
2512
|
+
children: "Usage"
|
|
2513
|
+
}), /* @__PURE__ */ jsxs(Alert, { children: [
|
|
2514
|
+
/* @__PURE__ */ jsx(HistoryIcon, { className: "size-4 shrink-0" }),
|
|
2515
|
+
/* @__PURE__ */ jsx(AlertTitle, { children: "This subscription has ended" }),
|
|
2516
|
+
/* @__PURE__ */ jsx(AlertDescription, { children: "Usage history isn't available yet." })
|
|
2517
|
+
] })]
|
|
2518
|
+
});
|
|
2519
|
+
const creditByFeature = new Map((pendingCredits ?? []).map((credit) => [credit.featureKey, credit]));
|
|
2343
2520
|
return /* @__PURE__ */ jsxs("div", {
|
|
2344
2521
|
className: "space-y-4",
|
|
2345
2522
|
children: [
|
|
@@ -2378,7 +2555,8 @@ const Usage = ({ usage, isFetching, currentItems, subscription, isPendingFirstPa
|
|
|
2378
2555
|
featureKey: key,
|
|
2379
2556
|
meter: { ...value },
|
|
2380
2557
|
subscription,
|
|
2381
|
-
item: currentItems?.find((item) => item.featureKey === key)
|
|
2558
|
+
item: currentItems?.find((item) => item.featureKey === key),
|
|
2559
|
+
pendingCredit: creditByFeature.get(key)
|
|
2382
2560
|
}, key) : []) : !isFetching && !subscription?.annotations?.["subscription.previous.id"] ? /* @__PURE__ */ jsxs(Alert, {
|
|
2383
2561
|
variant: "warning",
|
|
2384
2562
|
children: [
|
|
@@ -2401,6 +2579,7 @@ const ActiveSubscription = ({ subscription, deploymentName }) => {
|
|
|
2401
2579
|
refetchOnWindowFocus: true,
|
|
2402
2580
|
meta: { context: zudoku }
|
|
2403
2581
|
});
|
|
2582
|
+
const pendingCreditsQuery = usePendingCredits(deploymentName, subscription.id);
|
|
2404
2583
|
const isPendingFirstPayment = usageQuery.data.paymentStatus.isFirstPayment === true && usageQuery.data.paymentStatus.status !== "paid" && usageQuery.data.paymentStatus.status !== "not_required";
|
|
2405
2584
|
const activePhase = getActivePhase(subscription);
|
|
2406
2585
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
@@ -2419,7 +2598,8 @@ const ActiveSubscription = ({ subscription, deploymentName }) => {
|
|
|
2419
2598
|
usage: usageQuery.data,
|
|
2420
2599
|
isFetching: usageQuery.isFetching,
|
|
2421
2600
|
subscription,
|
|
2422
|
-
isPendingFirstPayment
|
|
2601
|
+
isPendingFirstPayment,
|
|
2602
|
+
pendingCredits: pendingCreditsQuery.data?.pendingCredits
|
|
2423
2603
|
}),
|
|
2424
2604
|
subscription?.consumer?.apiKeys && /* @__PURE__ */ jsx(ApiKeysList, {
|
|
2425
2605
|
isPendingFirstPayment,
|
|
@@ -2566,6 +2746,7 @@ const zuploMonetizationPlugin = createPlugin((options = {}) => ({
|
|
|
2566
2746
|
if (context.getAuthState().isAuthenticated) queryClient.prefetchQuery(subscriptionsQuery(context));
|
|
2567
2747
|
},
|
|
2568
2748
|
getIdentities: async (context) => {
|
|
2749
|
+
if (!context.getAuthState().isAuthenticated) return [];
|
|
2569
2750
|
return (await queryClient.fetchQuery(subscriptionsQuery(context))).items.flatMap((sub) => sub.status !== "active" ? [] : sub.consumer.apiKeys.flatMap((apiKey) => apiKey.expiresOn && new Date(apiKey.expiresOn) < /* @__PURE__ */ new Date() ? [] : {
|
|
2570
2751
|
label: `${sub.name} (****${apiKey.key.slice(-5)})`,
|
|
2571
2752
|
id: apiKey.id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zuplo/zudoku-plugin-monetization",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.45",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zuplo/zudoku",
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"@testing-library/react": "16.3.2",
|
|
34
34
|
"@types/react": "19.2.14",
|
|
35
35
|
"@types/react-dom": "19.2.3",
|
|
36
|
-
"happy-dom": "20.
|
|
36
|
+
"happy-dom": "20.10.2",
|
|
37
37
|
"react": "19.2.5",
|
|
38
38
|
"react-dom": "19.2.5",
|
|
39
39
|
"tsdown": "0.22.0",
|
|
40
|
-
"zudoku": "0.
|
|
40
|
+
"zudoku": "0.81.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"react": ">=19.2.0",
|