@lukoweb/apitogo-plugin-dev-portal-billing 0.1.86 → 0.1.88
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.d.mts +26 -15
- package/dist/index.mjs +280 -105
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -6,6 +6,32 @@ import * as _tanstack_react_query0 from "@tanstack/react-query";
|
|
|
6
6
|
declare const AccountPlansPage: () => react_jsx_runtime0.JSX.Element;
|
|
7
7
|
//#endregion
|
|
8
8
|
//#region src/types.d.ts
|
|
9
|
+
type DevPortalPricingCompareRow = {
|
|
10
|
+
label: string;
|
|
11
|
+
values: string[];
|
|
12
|
+
};
|
|
13
|
+
type DevPortalPricingFaq = {
|
|
14
|
+
question: string;
|
|
15
|
+
answer: string;
|
|
16
|
+
};
|
|
17
|
+
type DevPortalBillingConfig = {
|
|
18
|
+
pricing?: {
|
|
19
|
+
title?: string;
|
|
20
|
+
subtitle?: string;
|
|
21
|
+
compareRows?: DevPortalPricingCompareRow[];
|
|
22
|
+
faqs?: DevPortalPricingFaq[];
|
|
23
|
+
}; /** Local preview plan catalog (from `apitogo.config.json` `plans`). */
|
|
24
|
+
previewPlans?: Array<{
|
|
25
|
+
sku: string;
|
|
26
|
+
displayName: string;
|
|
27
|
+
priceAmount?: number;
|
|
28
|
+
priceCurrency?: string;
|
|
29
|
+
billingPeriod?: string;
|
|
30
|
+
limitsJson?: string | null;
|
|
31
|
+
includedActionKeys?: string[];
|
|
32
|
+
features?: string[];
|
|
33
|
+
}>;
|
|
34
|
+
};
|
|
9
35
|
type DevPortalApiKey = {
|
|
10
36
|
id: string;
|
|
11
37
|
name: string;
|
|
@@ -28,21 +54,6 @@ type DevPortalApiKeyListResponse = {
|
|
|
28
54
|
header: string;
|
|
29
55
|
gatewayBaseUrl: string | null;
|
|
30
56
|
};
|
|
31
|
-
type DevPortalBillingConfig = {
|
|
32
|
-
pricing?: {
|
|
33
|
-
title?: string;
|
|
34
|
-
subtitle?: string;
|
|
35
|
-
}; /** Local preview plan catalog (from `apitogo.config.json` `plans`). */
|
|
36
|
-
previewPlans?: Array<{
|
|
37
|
-
sku: string;
|
|
38
|
-
displayName: string;
|
|
39
|
-
priceAmount?: number;
|
|
40
|
-
priceCurrency?: string;
|
|
41
|
-
billingPeriod?: string;
|
|
42
|
-
limitsJson?: string | null;
|
|
43
|
-
includedActionKeys?: string[];
|
|
44
|
-
}>;
|
|
45
|
-
};
|
|
46
57
|
//#endregion
|
|
47
58
|
//#region src/components/ApiKeysPanel.d.ts
|
|
48
59
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -39,7 +39,8 @@ function mapPreviewPlans(plans) {
|
|
|
39
39
|
priceCurrency: plan.priceCurrency,
|
|
40
40
|
billingPeriod: plan.billingPeriod,
|
|
41
41
|
limitsJson: plan.limitsJson,
|
|
42
|
-
includedActionKeys: plan.includedActionKeys
|
|
42
|
+
includedActionKeys: plan.includedActionKeys,
|
|
43
|
+
features: plan.features
|
|
43
44
|
}));
|
|
44
45
|
}
|
|
45
46
|
function readModulePlanItems() {
|
|
@@ -54,7 +55,8 @@ function parseManifestPlans(configPlans) {
|
|
|
54
55
|
priceCurrency: plan.priceCurrency ?? "usd",
|
|
55
56
|
billingPeriod: plan.billingPeriod ?? "monthly",
|
|
56
57
|
limitsJson: plan.limitsJson ?? null,
|
|
57
|
-
includedActionKeys: plan.includedActionKeys ?? []
|
|
58
|
+
includedActionKeys: plan.includedActionKeys ?? [],
|
|
59
|
+
features: plan.features
|
|
58
60
|
}))) };
|
|
59
61
|
if (manifestCatalog?.plans?.length) return { plans: mapPreviewPlans(manifestCatalog.plans) };
|
|
60
62
|
const metaPlans = zudokuConfig?.__meta?.previewPlans;
|
|
@@ -67,7 +69,8 @@ function parseManifestPlans(configPlans) {
|
|
|
67
69
|
priceCurrency: plan.priceCurrency ?? "usd",
|
|
68
70
|
billingPeriod: plan.billingPeriod ?? "monthly",
|
|
69
71
|
limitsJson: plan.limitsJson ?? null,
|
|
70
|
-
includedActionKeys: plan.includedActionKeys ?? []
|
|
72
|
+
includedActionKeys: plan.includedActionKeys ?? [],
|
|
73
|
+
features: plan.features
|
|
71
74
|
}))) };
|
|
72
75
|
return { plans: [] };
|
|
73
76
|
}
|
|
@@ -77,6 +80,7 @@ function formatActionKey(actionKey) {
|
|
|
77
80
|
return `${actionKey.slice(0, separatorIndex).toUpperCase()} ${actionKey.slice(separatorIndex + 1)}`;
|
|
78
81
|
}
|
|
79
82
|
function formatPlanPrice(plan) {
|
|
83
|
+
if (plan.priceAmount < 0) return "Custom";
|
|
80
84
|
if (plan.priceAmount <= 0) return "Free";
|
|
81
85
|
const amount = new Intl.NumberFormat(void 0, {
|
|
82
86
|
style: "currency",
|
|
@@ -89,33 +93,63 @@ function formatPlanPrice(plan) {
|
|
|
89
93
|
return amount;
|
|
90
94
|
}
|
|
91
95
|
function isPlanFree(plan) {
|
|
92
|
-
return plan.priceAmount
|
|
96
|
+
return plan.priceAmount === 0;
|
|
93
97
|
}
|
|
94
98
|
function formatBillingPeriod(period) {
|
|
95
99
|
const normalized = period.toLowerCase();
|
|
96
|
-
if (normalized === "month" || normalized === "monthly") return "
|
|
97
|
-
if (normalized === "year" || normalized === "annual") return "
|
|
100
|
+
if (normalized === "month" || normalized === "monthly") return "per month";
|
|
101
|
+
if (normalized === "year" || normalized === "annual") return "per year";
|
|
102
|
+
if (normalized === "custom") return "talk to us";
|
|
98
103
|
return period;
|
|
99
104
|
}
|
|
100
105
|
function formatRateLimit(rate) {
|
|
101
106
|
if (!rate || typeof rate !== "object") return null;
|
|
102
107
|
const { calls, renewalPeriodSeconds } = rate;
|
|
103
108
|
if (calls == null || renewalPeriodSeconds == null) return null;
|
|
109
|
+
if (renewalPeriodSeconds === 1) return `${calls} req/sec rate limit`;
|
|
104
110
|
const window = renewalPeriodSeconds === 60 ? "minute" : `${renewalPeriodSeconds} seconds`;
|
|
105
111
|
return `${calls} request${calls === 1 ? "" : "s"} per ${window}`;
|
|
106
112
|
}
|
|
107
|
-
function
|
|
108
|
-
if (!
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
113
|
+
function formatQuota(quota) {
|
|
114
|
+
if (!quota || typeof quota !== "object") return null;
|
|
115
|
+
const { calls, renewalPeriodDays } = quota;
|
|
116
|
+
if (calls == null) return null;
|
|
117
|
+
if (calls < 0) return "Unlimited requests";
|
|
118
|
+
const formatted = calls.toLocaleString();
|
|
119
|
+
if (renewalPeriodDays === 30 || renewalPeriodDays === 31) return `${formatted} requests / month`;
|
|
120
|
+
if (renewalPeriodDays === 1) return `${formatted} requests / day`;
|
|
121
|
+
if (renewalPeriodDays != null) return `${formatted} requests / ${renewalPeriodDays} days`;
|
|
122
|
+
return `${formatted} requests`;
|
|
123
|
+
}
|
|
124
|
+
/** Marketing bullets keyed by common sample SKUs when limitsJson is sparse. */
|
|
125
|
+
const SAMPLE_PLAN_FEATURE_EXTRAS = {
|
|
126
|
+
free: ["All endpoints", "Community support"],
|
|
127
|
+
developer: ["Webhooks included", "Email support"],
|
|
128
|
+
team: ["5 team seats", "Priority support"],
|
|
129
|
+
enterprise: [
|
|
130
|
+
"Unlimited requests",
|
|
131
|
+
"Custom rate limits",
|
|
132
|
+
"SSO & audit logs",
|
|
133
|
+
"Dedicated SLA"
|
|
134
|
+
]
|
|
135
|
+
};
|
|
136
|
+
function listPlanFeatureLines(plan) {
|
|
137
|
+
if (plan.features && plan.features.length > 0) return plan.features;
|
|
138
|
+
const lines = [];
|
|
139
|
+
if (plan.limitsJson?.trim()) try {
|
|
140
|
+
const limits = JSON.parse(plan.limitsJson);
|
|
141
|
+
const quotaLine = formatQuota(limits.quota);
|
|
142
|
+
if (quotaLine) lines.push(quotaLine);
|
|
143
|
+
const rateLine = formatRateLimit(limits.rateLimit);
|
|
144
|
+
if (rateLine) lines.push(rateLine);
|
|
145
|
+
} catch {}
|
|
146
|
+
const extras = SAMPLE_PLAN_FEATURE_EXTRAS[plan.sku.toLowerCase()];
|
|
147
|
+
if (extras) {
|
|
148
|
+
for (const extra of extras) if (!lines.includes(extra)) lines.push(extra);
|
|
118
149
|
}
|
|
150
|
+
if (plan.includedActionKeys && plan.includedActionKeys.length > 0) for (const actionKey of plan.includedActionKeys) lines.push(formatActionKey(actionKey));
|
|
151
|
+
if (lines.length === 0 && isPlanFree(plan)) lines.push("API access not included");
|
|
152
|
+
return lines;
|
|
119
153
|
}
|
|
120
154
|
function isActiveSubscription(status) {
|
|
121
155
|
return status.toLowerCase() === "active";
|
|
@@ -123,55 +157,49 @@ function isActiveSubscription(status) {
|
|
|
123
157
|
|
|
124
158
|
//#endregion
|
|
125
159
|
//#region src/components/PricingCard.tsx
|
|
126
|
-
const
|
|
127
|
-
|
|
160
|
+
const publicCtaLabel = (plan) => {
|
|
161
|
+
if (plan.priceAmount < 0) return "Contact sales";
|
|
162
|
+
if (isPlanFree(plan)) return "Start for free";
|
|
163
|
+
return "Start free trial";
|
|
164
|
+
};
|
|
165
|
+
const PricingCard = ({ plan, mode = "public", subscriptionStatus, isSubscribed = false, onUnsubscribe, isCanceling = false, cancelDisabled = false, featured = false }) => {
|
|
128
166
|
const isActive = isSubscribed || isActiveSubscription(subscriptionStatus ?? "");
|
|
167
|
+
const features = listPlanFeatureLines(plan);
|
|
168
|
+
const priceLabel = formatPlanPrice(plan);
|
|
169
|
+
const isCustom = plan.priceAmount < 0;
|
|
170
|
+
const displayPrice = isCustom ? "Custom" : isPlanFree(plan) ? "$0" : priceLabel.replace(/\/mo$/, "").replace(/\/yr$/, "") || priceLabel;
|
|
129
171
|
return /* @__PURE__ */ jsxs("article", {
|
|
130
|
-
className: cn("flex
|
|
172
|
+
className: cn("flex h-full w-full flex-col rounded-xl border border-border bg-card p-7", featured && "border-primary/50 shadow-[0_10px_28px_rgba(13,148,136,0.28)] ring-1 ring-primary/25", isActive && mode === "account" && "border-primary/40 ring-1 ring-primary/20"),
|
|
131
173
|
"data-testid": `pricing-card-${plan.sku}`,
|
|
132
174
|
children: [
|
|
133
175
|
/* @__PURE__ */ jsxs("div", {
|
|
134
|
-
className: "flex items-
|
|
135
|
-
children: [
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
"
|
|
141
|
-
|
|
142
|
-
|
|
176
|
+
className: "flex items-center justify-between gap-2",
|
|
177
|
+
children: [
|
|
178
|
+
/* @__PURE__ */ jsx("h2", {
|
|
179
|
+
className: "text-sm font-bold uppercase tracking-[0.04em]",
|
|
180
|
+
children: plan.displayName
|
|
181
|
+
}),
|
|
182
|
+
featured && !isActive ? /* @__PURE__ */ jsx("span", {
|
|
183
|
+
className: "shrink-0 rounded-md bg-primary/10 px-2.5 py-0.5 text-xs font-semibold text-primary",
|
|
184
|
+
children: "Popular"
|
|
185
|
+
}) : null,
|
|
186
|
+
isActive && mode === "account" ? /* @__PURE__ */ jsx("span", {
|
|
187
|
+
className: "shrink-0 rounded-full bg-primary/10 px-3 py-1 text-xs font-medium text-primary",
|
|
188
|
+
"data-testid": `subscribed-badge-${plan.sku}`,
|
|
189
|
+
children: "Subscribed"
|
|
190
|
+
}) : null
|
|
191
|
+
]
|
|
143
192
|
}),
|
|
144
193
|
/* @__PURE__ */ jsx("p", {
|
|
145
|
-
className: "mt-
|
|
146
|
-
children:
|
|
194
|
+
className: "mt-3.5 text-[2.1rem] font-extrabold tracking-[-0.02em]",
|
|
195
|
+
children: displayPrice
|
|
147
196
|
}),
|
|
148
197
|
/* @__PURE__ */ jsx("p", {
|
|
149
|
-
className: "mt-
|
|
150
|
-
children: formatBillingPeriod(plan.billingPeriod)
|
|
198
|
+
className: "mb-[18px] mt-0.5 text-[0.78125rem] text-muted-foreground",
|
|
199
|
+
children: isCustom ? "talk to us" : formatBillingPeriod(plan.billingPeriod)
|
|
151
200
|
}),
|
|
152
|
-
limitsSummary ? /* @__PURE__ */ jsx("p", {
|
|
153
|
-
className: "mt-4 text-sm text-muted-foreground",
|
|
154
|
-
children: limitsSummary
|
|
155
|
-
}) : null,
|
|
156
|
-
plan.includedActionKeys && plan.includedActionKeys.length > 0 ? /* @__PURE__ */ jsxs("div", {
|
|
157
|
-
className: "mt-4",
|
|
158
|
-
"data-testid": `included-endpoints-${plan.sku}`,
|
|
159
|
-
children: [/* @__PURE__ */ jsx("p", {
|
|
160
|
-
className: "text-xs font-medium uppercase tracking-wide text-muted-foreground",
|
|
161
|
-
children: "Included endpoints"
|
|
162
|
-
}), /* @__PURE__ */ jsx("ul", {
|
|
163
|
-
className: "mt-2 space-y-1 text-sm text-muted-foreground",
|
|
164
|
-
children: plan.includedActionKeys.map((actionKey) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", {
|
|
165
|
-
className: "rounded bg-muted px-1.5 py-0.5 text-xs",
|
|
166
|
-
children: formatActionKey(actionKey)
|
|
167
|
-
}) }, actionKey))
|
|
168
|
-
})]
|
|
169
|
-
}) : isPlanFree(plan) ? /* @__PURE__ */ jsx("p", {
|
|
170
|
-
className: "mt-4 text-sm text-muted-foreground",
|
|
171
|
-
children: "API access not included"
|
|
172
|
-
}) : null,
|
|
173
201
|
/* @__PURE__ */ jsx("div", {
|
|
174
|
-
className: "
|
|
202
|
+
className: "space-y-2",
|
|
175
203
|
children: mode === "account" ? isActive ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
176
204
|
/* @__PURE__ */ jsx(Button, {
|
|
177
205
|
variant: "secondary",
|
|
@@ -202,7 +230,7 @@ const PricingCard = ({ plan, mode = "public", subscriptionStatus, isSubscribed =
|
|
|
202
230
|
children: "Unsubscribe is available after the portal is published."
|
|
203
231
|
}) : null
|
|
204
232
|
] }) : /* @__PURE__ */ jsx(Button, {
|
|
205
|
-
variant: "default",
|
|
233
|
+
variant: featured ? "default" : "outline",
|
|
206
234
|
className: "w-full",
|
|
207
235
|
asChild: true,
|
|
208
236
|
children: /* @__PURE__ */ jsx(Link, {
|
|
@@ -218,15 +246,27 @@ const PricingCard = ({ plan, mode = "public", subscriptionStatus, isSubscribed =
|
|
|
218
246
|
children: "Manage subscription"
|
|
219
247
|
})
|
|
220
248
|
}) : /* @__PURE__ */ jsx(Button, {
|
|
221
|
-
variant: "default",
|
|
249
|
+
variant: featured ? "default" : "outline",
|
|
222
250
|
className: "w-full",
|
|
223
251
|
asChild: true,
|
|
224
252
|
children: /* @__PURE__ */ jsx(Link, {
|
|
225
253
|
to: `/checkout?planSku=${encodeURIComponent(plan.sku)}`,
|
|
226
|
-
children:
|
|
254
|
+
children: publicCtaLabel(plan)
|
|
227
255
|
})
|
|
228
256
|
})
|
|
229
|
-
})
|
|
257
|
+
}),
|
|
258
|
+
features.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
259
|
+
className: "mt-5 flex flex-1 flex-col gap-[11px] border-t border-border pt-[18px]",
|
|
260
|
+
"data-testid": plan.includedActionKeys && plan.includedActionKeys.length > 0 ? `included-endpoints-${plan.sku}` : void 0,
|
|
261
|
+
children: features.map((feat) => /* @__PURE__ */ jsxs("div", {
|
|
262
|
+
className: "flex items-start gap-2.5 text-[0.84375rem] text-muted-foreground",
|
|
263
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
264
|
+
className: "mt-0.5 shrink-0 text-emerald-600",
|
|
265
|
+
"aria-hidden": true,
|
|
266
|
+
children: "✓"
|
|
267
|
+
}), /* @__PURE__ */ jsx("span", { children: feat })]
|
|
268
|
+
}, feat))
|
|
269
|
+
}) : null
|
|
230
270
|
]
|
|
231
271
|
});
|
|
232
272
|
};
|
|
@@ -1127,6 +1167,69 @@ function isPreviewPricingEnabled() {
|
|
|
1127
1167
|
|
|
1128
1168
|
//#endregion
|
|
1129
1169
|
//#region src/pages/PricingPage.tsx
|
|
1170
|
+
const DEFAULT_COMPARE_ROWS = [
|
|
1171
|
+
{
|
|
1172
|
+
label: "Monthly requests",
|
|
1173
|
+
values: [
|
|
1174
|
+
"1,000",
|
|
1175
|
+
"50,000",
|
|
1176
|
+
"500,000",
|
|
1177
|
+
"Unlimited"
|
|
1178
|
+
]
|
|
1179
|
+
},
|
|
1180
|
+
{
|
|
1181
|
+
label: "Rate limit",
|
|
1182
|
+
values: [
|
|
1183
|
+
"10/sec",
|
|
1184
|
+
"50/sec",
|
|
1185
|
+
"200/sec",
|
|
1186
|
+
"Custom"
|
|
1187
|
+
]
|
|
1188
|
+
},
|
|
1189
|
+
{
|
|
1190
|
+
label: "Team seats",
|
|
1191
|
+
values: [
|
|
1192
|
+
"1",
|
|
1193
|
+
"1",
|
|
1194
|
+
"5",
|
|
1195
|
+
"Unlimited"
|
|
1196
|
+
]
|
|
1197
|
+
},
|
|
1198
|
+
{
|
|
1199
|
+
label: "Webhooks",
|
|
1200
|
+
values: [
|
|
1201
|
+
"—",
|
|
1202
|
+
"Included",
|
|
1203
|
+
"Included",
|
|
1204
|
+
"Included"
|
|
1205
|
+
]
|
|
1206
|
+
},
|
|
1207
|
+
{
|
|
1208
|
+
label: "SSO",
|
|
1209
|
+
values: [
|
|
1210
|
+
"—",
|
|
1211
|
+
"—",
|
|
1212
|
+
"—",
|
|
1213
|
+
"Included"
|
|
1214
|
+
]
|
|
1215
|
+
},
|
|
1216
|
+
{
|
|
1217
|
+
label: "Support",
|
|
1218
|
+
values: [
|
|
1219
|
+
"Community",
|
|
1220
|
+
"Email",
|
|
1221
|
+
"Priority",
|
|
1222
|
+
"Dedicated SLA"
|
|
1223
|
+
]
|
|
1224
|
+
}
|
|
1225
|
+
];
|
|
1226
|
+
const DEFAULT_FAQS = [{
|
|
1227
|
+
question: "What happens if I exceed my quota?",
|
|
1228
|
+
answer: "Requests over your monthly quota return a 429 response so you can test rate-limit handling instead of being cut off unexpectedly."
|
|
1229
|
+
}, {
|
|
1230
|
+
question: "Can I change plans later?",
|
|
1231
|
+
answer: "Yes, upgrade or downgrade any time from your billing page. Changes apply on your next billing cycle."
|
|
1232
|
+
}];
|
|
1130
1233
|
const PricingPage = () => {
|
|
1131
1234
|
const { pricing, previewPlans } = useDevPortalBillingConfig();
|
|
1132
1235
|
const auth = useAuth();
|
|
@@ -1146,62 +1249,134 @@ const PricingPage = () => {
|
|
|
1146
1249
|
replace: true
|
|
1147
1250
|
});
|
|
1148
1251
|
const activeSkus = new Set((me?.subscriptions ?? []).filter((sub) => isActiveSubscription(sub.status)).map((sub) => sub.planSku));
|
|
1149
|
-
const title = pricing?.title ?? "
|
|
1150
|
-
const subtitle = pricing?.subtitle ?? "
|
|
1252
|
+
const title = pricing?.title ?? "Pay for what you call";
|
|
1253
|
+
const subtitle = pricing?.subtitle ?? "Every plan includes the full endpoint catalog. Higher tiers raise your request quota, rate limit, and support level.";
|
|
1254
|
+
const plans = catalog?.plans ?? [];
|
|
1255
|
+
const featuredSku = plans.find((p) => p.sku === "developer")?.sku ?? plans.find((p) => p.priceAmount > 0 && p.priceAmount < 50)?.sku ?? plans.find((p) => p.priceAmount > 0)?.sku;
|
|
1256
|
+
const compareRows = pricing?.compareRows && pricing.compareRows.length > 0 ? pricing.compareRows : DEFAULT_COMPARE_ROWS;
|
|
1257
|
+
const faqs = pricing?.faqs && pricing.faqs.length > 0 ? pricing.faqs : DEFAULT_FAQS;
|
|
1258
|
+
const compareHeaders = plans.length > 0 ? plans.map((p) => p.displayName) : [
|
|
1259
|
+
"Free",
|
|
1260
|
+
"Developer",
|
|
1261
|
+
"Team",
|
|
1262
|
+
"Enterprise"
|
|
1263
|
+
];
|
|
1151
1264
|
return /* @__PURE__ */ jsxs("div", {
|
|
1152
|
-
className: "w-full
|
|
1265
|
+
className: "w-full",
|
|
1153
1266
|
children: [
|
|
1154
1267
|
/* @__PURE__ */ jsxs(Head, { children: [/* @__PURE__ */ jsx("title", { children: title }), /* @__PURE__ */ jsx("meta", {
|
|
1155
1268
|
name: "description",
|
|
1156
1269
|
content: subtitle
|
|
1157
1270
|
})] }),
|
|
1158
|
-
/* @__PURE__ */ jsxs("
|
|
1159
|
-
className: "
|
|
1160
|
-
children: [/* @__PURE__ */ jsx(Heading, {
|
|
1161
|
-
level: 1,
|
|
1162
|
-
"data-testid": "title",
|
|
1163
|
-
children: title
|
|
1164
|
-
}), /* @__PURE__ */ jsx("p", {
|
|
1165
|
-
className: "text-muted-foreground",
|
|
1166
|
-
"data-testid": "subtitle",
|
|
1167
|
-
children: subtitle
|
|
1168
|
-
})]
|
|
1169
|
-
}),
|
|
1170
|
-
isLoading ? /* @__PURE__ */ jsx("p", {
|
|
1171
|
-
className: "text-center text-muted-foreground",
|
|
1172
|
-
children: "Loading plans…"
|
|
1173
|
-
}) : null,
|
|
1174
|
-
isError ? /* @__PURE__ */ jsx("p", {
|
|
1175
|
-
className: "text-center text-destructive",
|
|
1176
|
-
children: "Unable to load plans."
|
|
1177
|
-
}) : null,
|
|
1178
|
-
catalog && catalog.plans.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
1179
|
-
className: "grid grid-cols-1 justify-center gap-6 sm:grid-cols-[repeat(auto-fit,minmax(280px,max-content))]",
|
|
1180
|
-
children: catalog.plans.map((plan) => /* @__PURE__ */ jsx(PricingCard, {
|
|
1181
|
-
plan,
|
|
1182
|
-
mode: "public",
|
|
1183
|
-
isSubscribed: activeSkus.has(plan.sku)
|
|
1184
|
-
}, plan.sku))
|
|
1185
|
-
}) : !isLoading && !isError ? /* @__PURE__ */ jsxs("div", {
|
|
1186
|
-
className: "mx-auto max-w-lg rounded-xl border border-dashed p-8 text-center text-sm text-muted-foreground",
|
|
1271
|
+
/* @__PURE__ */ jsxs("section", {
|
|
1272
|
+
className: "px-6 pb-4 pt-[clamp(3rem,6vw,4.5rem)] text-center",
|
|
1187
1273
|
children: [
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
className: "rounded bg-muted px-1.5 py-0.5",
|
|
1192
|
-
children: "apitogo.config.json"
|
|
1274
|
+
/* @__PURE__ */ jsx("p", {
|
|
1275
|
+
className: "text-xs font-bold uppercase tracking-[0.08em] text-primary",
|
|
1276
|
+
children: "Pricing"
|
|
1193
1277
|
}),
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
children: "modules.userPanel.plans.enabled"
|
|
1278
|
+
/* @__PURE__ */ jsx(Heading, {
|
|
1279
|
+
level: 1,
|
|
1280
|
+
className: "mt-3 text-[clamp(2rem,3.5vw,3rem)] font-extrabold tracking-[-0.02em]",
|
|
1281
|
+
"data-testid": "title",
|
|
1282
|
+
children: title
|
|
1200
1283
|
}),
|
|
1201
|
-
|
|
1202
|
-
|
|
1284
|
+
/* @__PURE__ */ jsx("p", {
|
|
1285
|
+
className: "mx-auto mt-3.5 max-w-[34rem] text-lg leading-relaxed text-muted-foreground",
|
|
1286
|
+
"data-testid": "subtitle",
|
|
1287
|
+
children: subtitle
|
|
1288
|
+
})
|
|
1203
1289
|
]
|
|
1204
|
-
})
|
|
1290
|
+
}),
|
|
1291
|
+
/* @__PURE__ */ jsxs("section", {
|
|
1292
|
+
className: "px-6 pb-16 pt-8",
|
|
1293
|
+
children: [
|
|
1294
|
+
isLoading ? /* @__PURE__ */ jsx("p", {
|
|
1295
|
+
className: "text-center text-muted-foreground",
|
|
1296
|
+
children: "Loading plans…"
|
|
1297
|
+
}) : null,
|
|
1298
|
+
isError ? /* @__PURE__ */ jsx("p", {
|
|
1299
|
+
className: "text-center text-destructive",
|
|
1300
|
+
children: "Unable to load plans."
|
|
1301
|
+
}) : null,
|
|
1302
|
+
plans.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
1303
|
+
className: "mx-auto grid max-w-[1200px] grid-cols-1 items-stretch gap-5 sm:grid-cols-2 lg:grid-cols-4",
|
|
1304
|
+
children: plans.map((plan) => /* @__PURE__ */ jsx(PricingCard, {
|
|
1305
|
+
plan,
|
|
1306
|
+
mode: "public",
|
|
1307
|
+
isSubscribed: activeSkus.has(plan.sku),
|
|
1308
|
+
featured: plan.sku === featuredSku
|
|
1309
|
+
}, plan.sku))
|
|
1310
|
+
}) : !isLoading && !isError ? /* @__PURE__ */ jsxs("div", {
|
|
1311
|
+
className: "mx-auto max-w-lg rounded-xl border border-dashed p-8 text-center text-sm text-muted-foreground",
|
|
1312
|
+
children: [
|
|
1313
|
+
"No plans are configured yet. Add plans in",
|
|
1314
|
+
" ",
|
|
1315
|
+
/* @__PURE__ */ jsx("code", {
|
|
1316
|
+
className: "rounded bg-muted px-1.5 py-0.5",
|
|
1317
|
+
children: "apitogo.config.json"
|
|
1318
|
+
}),
|
|
1319
|
+
" ",
|
|
1320
|
+
"and set",
|
|
1321
|
+
" ",
|
|
1322
|
+
/* @__PURE__ */ jsx("code", {
|
|
1323
|
+
className: "rounded bg-muted px-1.5 py-0.5",
|
|
1324
|
+
children: "modules.userPanel.plans.enabled"
|
|
1325
|
+
}),
|
|
1326
|
+
" ",
|
|
1327
|
+
"to true."
|
|
1328
|
+
]
|
|
1329
|
+
}) : null
|
|
1330
|
+
]
|
|
1331
|
+
}),
|
|
1332
|
+
/* @__PURE__ */ jsx("section", {
|
|
1333
|
+
className: "border-t border-border bg-muted/40 px-6 py-16",
|
|
1334
|
+
"data-testid": "pricing-compare",
|
|
1335
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1336
|
+
className: "mx-auto max-w-[1200px]",
|
|
1337
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
1338
|
+
className: "mb-6 text-[clamp(1.5rem,2.5vw,2.25rem)] font-extrabold tracking-[-0.02em]",
|
|
1339
|
+
children: "Compare plans"
|
|
1340
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
1341
|
+
className: "overflow-x-auto rounded-xl border border-border bg-card",
|
|
1342
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1343
|
+
className: "min-w-[720px]",
|
|
1344
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1345
|
+
className: "grid gap-0 border-b border-border px-5 py-3.5 text-[0.78125rem] font-bold uppercase tracking-[0.04em] text-muted-foreground",
|
|
1346
|
+
style: { gridTemplateColumns: `minmax(10rem,1.4fr) repeat(${compareHeaders.length}, minmax(6rem,1fr))` },
|
|
1347
|
+
children: [/* @__PURE__ */ jsx("div", { children: "Feature" }), compareHeaders.map((header) => /* @__PURE__ */ jsx("div", { children: header }, header))]
|
|
1348
|
+
}), compareRows.map((row) => /* @__PURE__ */ jsxs("div", {
|
|
1349
|
+
className: "grid gap-0 border-b border-border px-5 py-3.5 text-[0.84375rem] text-muted-foreground last:border-b-0",
|
|
1350
|
+
style: { gridTemplateColumns: `minmax(10rem,1.4fr) repeat(${compareHeaders.length}, minmax(6rem,1fr))` },
|
|
1351
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1352
|
+
className: "font-medium text-foreground",
|
|
1353
|
+
children: row.label
|
|
1354
|
+
}), compareHeaders.map((_, index) => /* @__PURE__ */ jsx("div", { children: row.values[index] ?? "—" }, `${row.label}-${index}`))]
|
|
1355
|
+
}, row.label))]
|
|
1356
|
+
})
|
|
1357
|
+
})]
|
|
1358
|
+
})
|
|
1359
|
+
}),
|
|
1360
|
+
/* @__PURE__ */ jsx("section", {
|
|
1361
|
+
className: "px-6 py-16",
|
|
1362
|
+
"data-testid": "pricing-faq",
|
|
1363
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1364
|
+
className: "mx-auto max-w-4xl",
|
|
1365
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
1366
|
+
className: "mb-8 text-center text-[clamp(1.5rem,2.5vw,2.25rem)] font-extrabold tracking-[-0.02em]",
|
|
1367
|
+
children: "Frequently asked questions"
|
|
1368
|
+
}), /* @__PURE__ */ jsx("div", { children: faqs.map((faq) => /* @__PURE__ */ jsxs("div", {
|
|
1369
|
+
className: "border-b border-border py-5 last:border-b-0",
|
|
1370
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1371
|
+
className: "text-[0.9375rem] font-bold",
|
|
1372
|
+
children: faq.question
|
|
1373
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
1374
|
+
className: "mt-2 text-sm leading-relaxed text-muted-foreground",
|
|
1375
|
+
children: faq.answer
|
|
1376
|
+
})]
|
|
1377
|
+
}, faq.question)) })]
|
|
1378
|
+
})
|
|
1379
|
+
})
|
|
1205
1380
|
]
|
|
1206
1381
|
});
|
|
1207
1382
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lukoweb/apitogo-plugin-dev-portal-billing",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.88",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zuplo/zudoku",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"react": "19.2.4",
|
|
47
47
|
"react-dom": "19.2.4",
|
|
48
48
|
"tsdown": "0.20.3",
|
|
49
|
-
"@lukoweb/apitogo": "0.1.
|
|
49
|
+
"@lukoweb/apitogo": "0.1.88"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"react": ">=19.2.0",
|