@omnikit-js/ui 0.9.20 → 0.9.21
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/chunk-2MKNXIZG.mjs +6154 -0
- package/dist/chunk-2MKNXIZG.mjs.map +1 -0
- package/dist/chunk-FA7CH4TP.mjs +511 -0
- package/dist/chunk-FA7CH4TP.mjs.map +1 -0
- package/dist/{chunk-L7N56HNE.js → chunk-MTFFRTQW.js} +17 -17
- package/dist/chunk-MTFFRTQW.js.map +1 -0
- package/dist/{chunk-UAN65U7P.js → chunk-NHN5YSUE.js} +3 -3
- package/dist/{chunk-UAN65U7P.js.map → chunk-NHN5YSUE.js.map} +1 -1
- package/dist/components/client/index.css +160 -0
- package/dist/components/client/index.css.map +1 -0
- package/dist/components/client/index.js +2 -2
- package/dist/components/client/index.mjs +1350 -0
- package/dist/components/client/index.mjs.map +1 -0
- package/dist/components/server/index.css +160 -0
- package/dist/components/server/index.css.map +1 -0
- package/dist/components/server/index.js +2 -2
- package/dist/components/server/index.mjs +4 -0
- package/dist/components/server/index.mjs.map +1 -0
- package/dist/index.css +160 -0
- package/dist/index.css.map +1 -0
- package/dist/index.js +2 -2
- package/dist/index.mjs +4 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-L7N56HNE.js.map +0 -1
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import { Alert, Table, BillingContent } from './chunk-2MKNXIZG.mjs';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// src/lib/omnikit-client.ts
|
|
5
|
+
var OmniKitClient = class {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
8
|
+
this.apiKey = config.apiKey;
|
|
9
|
+
this.jwt = config.jwt;
|
|
10
|
+
}
|
|
11
|
+
getHeaders() {
|
|
12
|
+
const headers = {
|
|
13
|
+
"Content-Type": "application/json"
|
|
14
|
+
};
|
|
15
|
+
if (this.jwt) {
|
|
16
|
+
headers["Authorization"] = `Bearer ${this.jwt}`;
|
|
17
|
+
} else if (this.apiKey) {
|
|
18
|
+
headers["X-API-Key"] = this.apiKey;
|
|
19
|
+
}
|
|
20
|
+
return headers;
|
|
21
|
+
}
|
|
22
|
+
async request(endpoint, options) {
|
|
23
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
24
|
+
const response = await fetch(url, {
|
|
25
|
+
...options,
|
|
26
|
+
headers: {
|
|
27
|
+
...this.getHeaders(),
|
|
28
|
+
...options?.headers
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
const error = await response.json().catch(() => ({ error: "Request failed" }));
|
|
33
|
+
throw new Error(error.error || `HTTP ${response.status}`);
|
|
34
|
+
}
|
|
35
|
+
return response.json();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Query records from a table
|
|
39
|
+
*/
|
|
40
|
+
async query(table, options) {
|
|
41
|
+
const params = new URLSearchParams();
|
|
42
|
+
if (options?.filter) {
|
|
43
|
+
params.append("filter", JSON.stringify(options.filter));
|
|
44
|
+
}
|
|
45
|
+
if (options?.sort) {
|
|
46
|
+
params.append("sort", options.sort);
|
|
47
|
+
}
|
|
48
|
+
if (options?.limit) {
|
|
49
|
+
params.append("limit", options.limit.toString());
|
|
50
|
+
}
|
|
51
|
+
if (options?.offset) {
|
|
52
|
+
params.append("offset", options.offset.toString());
|
|
53
|
+
}
|
|
54
|
+
const queryString = params.toString();
|
|
55
|
+
const endpoint = `/data/${table}${queryString ? `?${queryString}` : ""}`;
|
|
56
|
+
return this.request(endpoint);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Find a record by ID
|
|
60
|
+
*/
|
|
61
|
+
async findById(table, id) {
|
|
62
|
+
return this.request(`/data/${table}/${id}`);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Insert a new record
|
|
66
|
+
*/
|
|
67
|
+
async insert(table, data) {
|
|
68
|
+
return this.request(`/data/${table}`, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
body: JSON.stringify(data)
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Update an existing record
|
|
75
|
+
*/
|
|
76
|
+
async update(table, id, data) {
|
|
77
|
+
return this.request(`/data/${table}/${id}`, {
|
|
78
|
+
method: "PUT",
|
|
79
|
+
body: JSON.stringify(data)
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Delete a record
|
|
84
|
+
*/
|
|
85
|
+
async delete(table, id) {
|
|
86
|
+
return this.request(`/data/${table}/${id}`, {
|
|
87
|
+
method: "DELETE"
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Upsert a record (insert or update)
|
|
92
|
+
*/
|
|
93
|
+
async upsert(table, data) {
|
|
94
|
+
return this.request(`/data/${table}`, {
|
|
95
|
+
method: "PUT",
|
|
96
|
+
body: JSON.stringify(data)
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
function createOmniKitClient(config) {
|
|
101
|
+
return new OmniKitClient(config);
|
|
102
|
+
}
|
|
103
|
+
function normalizeColumn(column) {
|
|
104
|
+
if (typeof column === "string") {
|
|
105
|
+
return {
|
|
106
|
+
key: column,
|
|
107
|
+
label: column.charAt(0).toUpperCase() + column.slice(1).replace(/_/g, " "),
|
|
108
|
+
sortable: true
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
...column,
|
|
113
|
+
label: column.label || column.key.charAt(0).toUpperCase() + column.key.slice(1).replace(/_/g, " ")
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async function DataList({
|
|
117
|
+
table,
|
|
118
|
+
baseUrl,
|
|
119
|
+
apiKey,
|
|
120
|
+
jwt,
|
|
121
|
+
columns,
|
|
122
|
+
keyField = "id",
|
|
123
|
+
filter,
|
|
124
|
+
sort,
|
|
125
|
+
limit,
|
|
126
|
+
offset,
|
|
127
|
+
className = "",
|
|
128
|
+
emptyMessage = "No data found",
|
|
129
|
+
errorMessage = "Failed to load data"
|
|
130
|
+
}) {
|
|
131
|
+
const client = createOmniKitClient({ baseUrl, apiKey, jwt });
|
|
132
|
+
let data = [];
|
|
133
|
+
let error = null;
|
|
134
|
+
try {
|
|
135
|
+
const response = await client.query(table, {
|
|
136
|
+
filter,
|
|
137
|
+
sort,
|
|
138
|
+
limit,
|
|
139
|
+
offset
|
|
140
|
+
});
|
|
141
|
+
if (response.success) {
|
|
142
|
+
data = response.data;
|
|
143
|
+
} else {
|
|
144
|
+
error = response.error || errorMessage;
|
|
145
|
+
}
|
|
146
|
+
} catch (err) {
|
|
147
|
+
error = err instanceof Error ? err.message : errorMessage;
|
|
148
|
+
}
|
|
149
|
+
const normalizedColumns = columns.map(normalizeColumn);
|
|
150
|
+
const processedData = data.map((row) => {
|
|
151
|
+
const processedRow = { ...row };
|
|
152
|
+
normalizedColumns.forEach((col) => {
|
|
153
|
+
if (col.render) {
|
|
154
|
+
const value = col.key.split(".").reduce((obj, k) => obj?.[k], row);
|
|
155
|
+
processedRow[`__rendered_${col.key}`] = col.render(value, row);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
return processedRow;
|
|
159
|
+
});
|
|
160
|
+
const tableColumns = normalizedColumns.map((col) => ({
|
|
161
|
+
key: col.render ? `__rendered_${col.key}` : col.key,
|
|
162
|
+
title: col.label || col.key
|
|
163
|
+
}));
|
|
164
|
+
if (error) {
|
|
165
|
+
return /* @__PURE__ */ jsxs(Alert, { variant: "error", className, children: [
|
|
166
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium", children: "Error" }),
|
|
167
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm", children: error })
|
|
168
|
+
] });
|
|
169
|
+
}
|
|
170
|
+
if (data.length === 0) {
|
|
171
|
+
return /* @__PURE__ */ jsx(Alert, { variant: "info", className, children: emptyMessage });
|
|
172
|
+
}
|
|
173
|
+
return /* @__PURE__ */ jsx(
|
|
174
|
+
Table,
|
|
175
|
+
{
|
|
176
|
+
columns: tableColumns,
|
|
177
|
+
data: processedData,
|
|
178
|
+
keyField: String(keyField),
|
|
179
|
+
hoverable: true,
|
|
180
|
+
responsive: true,
|
|
181
|
+
className
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/components/server/Billing/client.ts
|
|
187
|
+
function createHeaders(config) {
|
|
188
|
+
const headers = {
|
|
189
|
+
"Content-Type": "application/json"
|
|
190
|
+
};
|
|
191
|
+
if (config.accessToken) {
|
|
192
|
+
headers["Authorization"] = `Bearer ${config.accessToken}`;
|
|
193
|
+
}
|
|
194
|
+
if (config.apiKey) {
|
|
195
|
+
headers["X-API-Key"] = config.apiKey;
|
|
196
|
+
}
|
|
197
|
+
return headers;
|
|
198
|
+
}
|
|
199
|
+
async function fetchAPI(config, url, options = {}) {
|
|
200
|
+
try {
|
|
201
|
+
const response = await fetch(url, {
|
|
202
|
+
...options,
|
|
203
|
+
headers: {
|
|
204
|
+
...createHeaders(config),
|
|
205
|
+
...options.headers
|
|
206
|
+
},
|
|
207
|
+
// Prevent infinite re-fetching in Next.js
|
|
208
|
+
cache: "no-store"
|
|
209
|
+
});
|
|
210
|
+
if (!response.ok) {
|
|
211
|
+
try {
|
|
212
|
+
const errorData = await response.json();
|
|
213
|
+
return {
|
|
214
|
+
success: false,
|
|
215
|
+
error: errorData.error || `HTTP ${response.status}: ${response.statusText}`
|
|
216
|
+
};
|
|
217
|
+
} catch {
|
|
218
|
+
return {
|
|
219
|
+
success: false,
|
|
220
|
+
error: `HTTP ${response.status}: ${response.statusText}`
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const contentType = response.headers.get("content-type");
|
|
225
|
+
if (!contentType || !contentType.includes("application/json")) {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
error: "Invalid response: expected JSON"
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const data = await response.json();
|
|
232
|
+
return data;
|
|
233
|
+
} catch (error) {
|
|
234
|
+
return {
|
|
235
|
+
success: false,
|
|
236
|
+
error: error instanceof Error ? error.message : "API request failed"
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async function getOrCreateCustomer(config, userId, organizationId, email, name, testMode = true) {
|
|
241
|
+
const { baseUrl, projectId } = config;
|
|
242
|
+
const customerData = await fetchAPI(
|
|
243
|
+
config,
|
|
244
|
+
`${baseUrl}/billing/customers/by-user/${userId}?project_id=${projectId}`
|
|
245
|
+
);
|
|
246
|
+
if (customerData.success && customerData.data) {
|
|
247
|
+
return customerData;
|
|
248
|
+
}
|
|
249
|
+
const createData = await fetchAPI(
|
|
250
|
+
config,
|
|
251
|
+
`${baseUrl}/billing/customers`,
|
|
252
|
+
{
|
|
253
|
+
method: "POST",
|
|
254
|
+
body: JSON.stringify({
|
|
255
|
+
project_id: projectId,
|
|
256
|
+
user_id: userId,
|
|
257
|
+
organization_id: organizationId,
|
|
258
|
+
email,
|
|
259
|
+
name,
|
|
260
|
+
test_mode: testMode,
|
|
261
|
+
sync_to_stripe: true
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
return createData;
|
|
266
|
+
}
|
|
267
|
+
async function fetchUserData(config, userId) {
|
|
268
|
+
const { baseUrl } = config;
|
|
269
|
+
return fetchAPI(
|
|
270
|
+
config,
|
|
271
|
+
`${baseUrl}/data/users/${userId}`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
async function fetchBillingData(props) {
|
|
275
|
+
const {
|
|
276
|
+
baseUrl,
|
|
277
|
+
apiKey,
|
|
278
|
+
accessToken,
|
|
279
|
+
projectId,
|
|
280
|
+
userId,
|
|
281
|
+
organizationId,
|
|
282
|
+
userEmail,
|
|
283
|
+
userName,
|
|
284
|
+
testMode = true
|
|
285
|
+
} = props;
|
|
286
|
+
const config = { baseUrl, apiKey, accessToken, projectId };
|
|
287
|
+
try {
|
|
288
|
+
let email = userEmail;
|
|
289
|
+
let name = userName;
|
|
290
|
+
let orgId = organizationId;
|
|
291
|
+
if (!email) {
|
|
292
|
+
const userResult = await fetchUserData(config, userId);
|
|
293
|
+
if (userResult.success && userResult.data) {
|
|
294
|
+
email = userResult.data.email;
|
|
295
|
+
name = name || userResult.data.full_name || userResult.data.display_name;
|
|
296
|
+
orgId = orgId || userResult.data.organization_id;
|
|
297
|
+
} else {
|
|
298
|
+
return { success: false, error: userResult.error || "User not found. Please provide userEmail." };
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const finalOrgId = orgId || 1;
|
|
302
|
+
const customerResult = await getOrCreateCustomer(
|
|
303
|
+
config,
|
|
304
|
+
userId,
|
|
305
|
+
finalOrgId,
|
|
306
|
+
email,
|
|
307
|
+
name,
|
|
308
|
+
testMode
|
|
309
|
+
);
|
|
310
|
+
if (!customerResult.success || !customerResult.data) {
|
|
311
|
+
return { success: false, error: customerResult.error || "Failed to get customer" };
|
|
312
|
+
}
|
|
313
|
+
const customer = customerResult.data;
|
|
314
|
+
const customerId = customer.id;
|
|
315
|
+
const [
|
|
316
|
+
subsData,
|
|
317
|
+
productsData,
|
|
318
|
+
pricesData,
|
|
319
|
+
entitlementsData,
|
|
320
|
+
invoicesData,
|
|
321
|
+
pmData,
|
|
322
|
+
usageData
|
|
323
|
+
] = await Promise.all([
|
|
324
|
+
fetchAPI(config, `${baseUrl}/billing/subscriptions?project_id=${projectId}&customer_id=${customerId}&status=active&include_items=true`),
|
|
325
|
+
fetchAPI(config, `${baseUrl}/billing/products?project_id=${projectId}&limit=100&include_prices=false`),
|
|
326
|
+
fetchAPI(config, `${baseUrl}/billing/products/prices?project_id=${projectId}&limit=1000`),
|
|
327
|
+
fetchAPI(config, `${baseUrl}/billing/entitlements/list-entitlements?project_id=${projectId}&customer_id=${customerId}`),
|
|
328
|
+
fetchAPI(config, `${baseUrl}/billing/invoices?project_id=${projectId}&customer_id=${customerId}&limit=10&sort=due_date:desc&include_line_items=true`),
|
|
329
|
+
fetchAPI(config, `${baseUrl}/billing/customers/${customerId}/payment-methods?project_id=${projectId}&status=active`),
|
|
330
|
+
fetchAPI(config, `${baseUrl}/billing/usage/get-all?project_id=${projectId}&customer_id=${customerId}`)
|
|
331
|
+
]);
|
|
332
|
+
const currentSubscription = subsData.success && subsData.data && subsData.data.length > 0 ? subsData.data[0] : null;
|
|
333
|
+
const products = productsData.success && productsData.data ? productsData.data : [];
|
|
334
|
+
const allPrices = pricesData.success && pricesData.data ? pricesData.data : [];
|
|
335
|
+
const entitlements = entitlementsData.success && entitlementsData.data ? entitlementsData.data : [];
|
|
336
|
+
const invoices = invoicesData.success && invoicesData.data ? invoicesData.data : [];
|
|
337
|
+
const paymentMethods = pmData.success && pmData.data ? pmData.data : [];
|
|
338
|
+
const featureUsage = usageData.success && usageData.data ? usageData.data : {};
|
|
339
|
+
const pricesByProduct = {};
|
|
340
|
+
allPrices.forEach((price) => {
|
|
341
|
+
if (!pricesByProduct[price.product_id]) {
|
|
342
|
+
pricesByProduct[price.product_id] = [];
|
|
343
|
+
}
|
|
344
|
+
pricesByProduct[price.product_id].push(price);
|
|
345
|
+
});
|
|
346
|
+
products.forEach((product) => {
|
|
347
|
+
product.prices = pricesByProduct[product.id] || [];
|
|
348
|
+
});
|
|
349
|
+
const productFeatures = {};
|
|
350
|
+
await Promise.all(
|
|
351
|
+
products.map(async (product) => {
|
|
352
|
+
const featuresData = await fetchAPI(
|
|
353
|
+
config,
|
|
354
|
+
`${baseUrl}/billing/features/product/${product.id}?project_id=${projectId}&limit=100`
|
|
355
|
+
);
|
|
356
|
+
productFeatures[product.id] = featuresData.success && featuresData.data ? featuresData.data : [];
|
|
357
|
+
})
|
|
358
|
+
);
|
|
359
|
+
const billingData = {
|
|
360
|
+
customer,
|
|
361
|
+
currentSubscription,
|
|
362
|
+
products,
|
|
363
|
+
productFeatures,
|
|
364
|
+
entitlements,
|
|
365
|
+
invoices,
|
|
366
|
+
paymentMethods,
|
|
367
|
+
featureUsage
|
|
368
|
+
};
|
|
369
|
+
return { success: true, data: billingData };
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.error("Error fetching billing data:", error);
|
|
372
|
+
return {
|
|
373
|
+
success: false,
|
|
374
|
+
error: error instanceof Error ? error.message : "Failed to fetch billing data"
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
async function fetchTaxInfo(config, customerId, referenceAmount = 1e4) {
|
|
379
|
+
const { baseUrl, projectId } = config;
|
|
380
|
+
return fetchAPI(
|
|
381
|
+
config,
|
|
382
|
+
`${baseUrl}/billing/tax/calculate`,
|
|
383
|
+
{
|
|
384
|
+
method: "POST",
|
|
385
|
+
body: JSON.stringify({
|
|
386
|
+
project_id: projectId,
|
|
387
|
+
customer_id: customerId,
|
|
388
|
+
amount: referenceAmount,
|
|
389
|
+
product_type: "digital"
|
|
390
|
+
})
|
|
391
|
+
}
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
async function Billing(props) {
|
|
395
|
+
const {
|
|
396
|
+
className = "",
|
|
397
|
+
errorMessage = "Failed to load billing information"
|
|
398
|
+
} = props;
|
|
399
|
+
const result = await fetchBillingData(props);
|
|
400
|
+
if (!result.success || !result.data) {
|
|
401
|
+
return /* @__PURE__ */ jsxs(Alert, { variant: "error", className, children: [
|
|
402
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium", children: "Error" }),
|
|
403
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm", children: result.error || errorMessage })
|
|
404
|
+
] });
|
|
405
|
+
}
|
|
406
|
+
const { data } = result;
|
|
407
|
+
let initialTaxInfo = null;
|
|
408
|
+
if (data.customer?.id) {
|
|
409
|
+
const taxConfig = {
|
|
410
|
+
baseUrl: props.baseUrl,
|
|
411
|
+
apiKey: props.apiKey,
|
|
412
|
+
accessToken: props.accessToken,
|
|
413
|
+
projectId: props.projectId
|
|
414
|
+
};
|
|
415
|
+
const taxResult = await fetchTaxInfo(taxConfig, data.customer.id, 1e4);
|
|
416
|
+
if (taxResult.success && taxResult.data) {
|
|
417
|
+
initialTaxInfo = taxResult.data;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const currentSubscriptionPriceId = data.currentSubscription?.items?.[0]?.price_id;
|
|
421
|
+
const currentPlan = currentSubscriptionPriceId ? data.products.find((p) => p.prices?.some((price) => price.id === currentSubscriptionPriceId)) : null;
|
|
422
|
+
const currentPrice = currentPlan?.prices?.find((p) => p.id === currentSubscriptionPriceId);
|
|
423
|
+
const subscription = data.currentSubscription ? {
|
|
424
|
+
id: data.currentSubscription.id,
|
|
425
|
+
plan: currentPlan?.name || "Unknown Plan",
|
|
426
|
+
status: data.currentSubscription.status,
|
|
427
|
+
price: formatCurrency(
|
|
428
|
+
currentPrice?.unit_amount || currentPrice?.amount || 0,
|
|
429
|
+
data.currentSubscription.currency
|
|
430
|
+
),
|
|
431
|
+
period: currentPrice?.recurring_interval || "month",
|
|
432
|
+
nextBilling: formatDate(data.currentSubscription.current_period_end),
|
|
433
|
+
cancelAtPeriodEnd: data.currentSubscription.cancel_at_period_end
|
|
434
|
+
} : null;
|
|
435
|
+
const invoices = data.invoices.map((inv) => ({
|
|
436
|
+
id: inv.number || inv.uuid,
|
|
437
|
+
date: formatDate(inv.due_date || inv.created_at),
|
|
438
|
+
amount: formatCurrency(inv.total || inv.amount_due, inv.currency),
|
|
439
|
+
status: inv.status,
|
|
440
|
+
url: inv.pdf_url || "#"
|
|
441
|
+
}));
|
|
442
|
+
const plans = data.products.filter((p) => p.active && p.prices && p.prices.length > 0).map((product) => {
|
|
443
|
+
const price = product.prices?.find((p) => p.active) || product.prices?.[0];
|
|
444
|
+
const features = data.productFeatures[product.id] || [];
|
|
445
|
+
const isCurrentPlan = currentSubscriptionPriceId ? product.prices?.some((p) => p.id === currentSubscriptionPriceId) : false;
|
|
446
|
+
const priceAmount = price?.unit_amount || 0;
|
|
447
|
+
return {
|
|
448
|
+
id: product.id,
|
|
449
|
+
priceId: price?.id || 0,
|
|
450
|
+
name: product.name,
|
|
451
|
+
description: product.description,
|
|
452
|
+
price: formatCurrency(priceAmount, price?.currency || "usd"),
|
|
453
|
+
priceAmount,
|
|
454
|
+
// Raw amount in cents for sorting
|
|
455
|
+
period: price?.recurring_interval || "month",
|
|
456
|
+
features: features.map((f) => {
|
|
457
|
+
const featureName = f.feature_name || f.feature_key || "Feature";
|
|
458
|
+
const featureValue = typeof f.value === "object" && f.value !== null && "value" in f.value ? f.value.value : f.value;
|
|
459
|
+
const featureUnit = f.feature_unit || "";
|
|
460
|
+
if (featureValue !== void 0 && featureValue !== null) {
|
|
461
|
+
const formattedValue = typeof featureValue === "number" ? featureValue.toLocaleString() : String(featureValue);
|
|
462
|
+
return `${featureName}: ${formattedValue}${featureUnit ? ` ${featureUnit}` : ""}`.trim();
|
|
463
|
+
}
|
|
464
|
+
return featureName;
|
|
465
|
+
}),
|
|
466
|
+
current: isCurrentPlan || false,
|
|
467
|
+
popular: product.name.toLowerCase().includes("pro") || product.name.toLowerCase().includes("professional")
|
|
468
|
+
};
|
|
469
|
+
}).sort((a, b) => a.priceAmount - b.priceAmount);
|
|
470
|
+
const actionConfig = {
|
|
471
|
+
baseUrl: props.baseUrl,
|
|
472
|
+
apiKey: props.apiKey,
|
|
473
|
+
accessToken: props.accessToken,
|
|
474
|
+
projectId: props.projectId
|
|
475
|
+
};
|
|
476
|
+
return /* @__PURE__ */ jsx(
|
|
477
|
+
BillingContent,
|
|
478
|
+
{
|
|
479
|
+
subscription,
|
|
480
|
+
invoices,
|
|
481
|
+
paymentMethods: data.paymentMethods,
|
|
482
|
+
plans,
|
|
483
|
+
customer: data.customer,
|
|
484
|
+
entitlements: data.entitlements,
|
|
485
|
+
featureUsage: data.featureUsage,
|
|
486
|
+
actionConfig,
|
|
487
|
+
initialTaxInfo,
|
|
488
|
+
className
|
|
489
|
+
}
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
function formatCurrency(amount, currency = "usd") {
|
|
493
|
+
const formatter = new Intl.NumberFormat("en-US", {
|
|
494
|
+
style: "currency",
|
|
495
|
+
currency: currency.toUpperCase()
|
|
496
|
+
});
|
|
497
|
+
return formatter.format(amount / 100);
|
|
498
|
+
}
|
|
499
|
+
function formatDate(dateString) {
|
|
500
|
+
if (!dateString) return "N/A";
|
|
501
|
+
const date = new Date(dateString);
|
|
502
|
+
return date.toLocaleDateString("en-US", {
|
|
503
|
+
year: "numeric",
|
|
504
|
+
month: "long",
|
|
505
|
+
day: "numeric"
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
export { Billing, DataList, OmniKitClient, createOmniKitClient };
|
|
510
|
+
//# sourceMappingURL=chunk-FA7CH4TP.mjs.map
|
|
511
|
+
//# sourceMappingURL=chunk-FA7CH4TP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/omnikit-client.ts","../src/components/server/DataList/index.tsx","../src/components/server/Billing/client.ts","../src/components/server/Billing/index.tsx"],"names":["jsxs","jsx"],"mappings":"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","file":"chunk-FA7CH4TP.mjs","sourcesContent":["/**\n * OmniKit API Client\n * Wrapper for making requests to OmniKit serverless platform\n */\n\nimport type {\n QueryOptions,\n QueryResponse,\n SingleRecordResponse,\n MutationResponse,\n OmniKitConfig,\n} from './types'\n\nexport class OmniKitClient {\n private baseUrl: string\n private apiKey?: string\n private jwt?: string\n\n constructor(config: OmniKitConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, '') // Remove trailing slash\n this.apiKey = config.apiKey\n this.jwt = config.jwt\n }\n\n private getHeaders(): HeadersInit {\n const headers: HeadersInit = {\n 'Content-Type': 'application/json',\n }\n\n if (this.jwt) {\n headers['Authorization'] = `Bearer ${this.jwt}`\n } else if (this.apiKey) {\n headers['X-API-Key'] = this.apiKey\n }\n\n return headers\n }\n\n private async request<T>(\n endpoint: string,\n options?: RequestInit\n ): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`\n\n const response = await fetch(url, {\n ...options,\n headers: {\n ...this.getHeaders(),\n ...options?.headers,\n },\n })\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ error: 'Request failed' }))\n throw new Error(error.error || `HTTP ${response.status}`)\n }\n\n return response.json()\n }\n\n /**\n * Query records from a table\n */\n async query<T = any>(\n table: string,\n options?: QueryOptions\n ): Promise<QueryResponse<T>> {\n const params = new URLSearchParams()\n\n if (options?.filter) {\n params.append('filter', JSON.stringify(options.filter))\n }\n if (options?.sort) {\n params.append('sort', options.sort)\n }\n if (options?.limit) {\n params.append('limit', options.limit.toString())\n }\n if (options?.offset) {\n params.append('offset', options.offset.toString())\n }\n\n const queryString = params.toString()\n const endpoint = `/data/${table}${queryString ? `?${queryString}` : ''}`\n\n return this.request<QueryResponse<T>>(endpoint)\n }\n\n /**\n * Find a record by ID\n */\n async findById<T = any>(\n table: string,\n id: string | number\n ): Promise<SingleRecordResponse<T>> {\n return this.request<SingleRecordResponse<T>>(`/data/${table}/${id}`)\n }\n\n /**\n * Insert a new record\n */\n async insert(\n table: string,\n data: Record<string, any>\n ): Promise<MutationResponse> {\n return this.request<MutationResponse>(`/data/${table}`, {\n method: 'POST',\n body: JSON.stringify(data),\n })\n }\n\n /**\n * Update an existing record\n */\n async update(\n table: string,\n id: string | number,\n data: Record<string, any>\n ): Promise<MutationResponse> {\n return this.request<MutationResponse>(`/data/${table}/${id}`, {\n method: 'PUT',\n body: JSON.stringify(data),\n })\n }\n\n /**\n * Delete a record\n */\n async delete(table: string, id: string | number): Promise<MutationResponse> {\n return this.request<MutationResponse>(`/data/${table}/${id}`, {\n method: 'DELETE',\n })\n }\n\n /**\n * Upsert a record (insert or update)\n */\n async upsert(\n table: string,\n data: Record<string, any>\n ): Promise<MutationResponse> {\n return this.request<MutationResponse>(`/data/${table}`, {\n method: 'PUT',\n body: JSON.stringify(data),\n })\n }\n}\n\n/**\n * Create a new OmniKit client instance\n */\nexport function createOmniKitClient(config: OmniKitConfig): OmniKitClient {\n return new OmniKitClient(config)\n}\n","/**\n * DataList Component\n *\n * A server-side React component that fetches and displays data from OmniKit API.\n * Renders on the server with zero client JavaScript for data fetching.\n * Uses lite-ui Table component for consistent styling.\n *\n * @example\n * ```tsx\n * import { DataList } from '@omnikit-js/ui'\n *\n * export default function UsersPage() {\n * return (\n * <DataList\n * table=\"users\"\n * baseUrl={process.env.OMNIKIT_BASE_URL}\n * apiKey={process.env.OMNIKIT_API_KEY}\n * columns={['name', 'email', 'created_at']}\n * filter={{ active: true }}\n * limit={20}\n * />\n * )\n * }\n * ```\n */\n\nimport { Table, Alert } from '@marcoschwartz/lite-ui'\nimport { createOmniKitClient } from '../../../lib/omnikit-client'\nimport type { DataListProps, Column, ColumnDefinition } from './types'\n\nfunction normalizeColumn<T>(column: Column<T>): ColumnDefinition<T> {\n if (typeof column === 'string') {\n return {\n key: column,\n label: column.charAt(0).toUpperCase() + column.slice(1).replace(/_/g, ' '),\n sortable: true,\n }\n }\n return {\n ...column,\n label: column.label || column.key.charAt(0).toUpperCase() + column.key.slice(1).replace(/_/g, ' '),\n }\n}\n\nexport async function DataList<T extends Record<string, any>>({\n table,\n baseUrl,\n apiKey,\n jwt,\n columns,\n keyField = 'id' as keyof T,\n filter,\n sort,\n limit,\n offset,\n className = '',\n emptyMessage = 'No data found',\n errorMessage = 'Failed to load data',\n}: DataListProps<T>) {\n // Create API client\n const client = createOmniKitClient({ baseUrl, apiKey, jwt })\n\n // Fetch data\n let data: T[] = []\n let error: string | null = null\n\n try {\n const response = await client.query<T>(table, {\n filter,\n sort,\n limit,\n offset,\n })\n\n if (response.success) {\n data = response.data\n } else {\n error = response.error || errorMessage\n }\n } catch (err) {\n error = err instanceof Error ? err.message : errorMessage\n }\n\n // Normalize columns for lite-ui Table format\n const normalizedColumns = columns.map(normalizeColumn)\n\n // Pre-render data with custom render functions (since we can't pass functions to client components)\n const processedData = data.map((row) => {\n const processedRow: Record<string, any> = { ...row }\n normalizedColumns.forEach((col) => {\n if (col.render) {\n const value = col.key.split('.').reduce((obj, k) => obj?.[k], row)\n processedRow[`__rendered_${col.key}`] = col.render(value, row)\n }\n })\n return processedRow\n })\n\n const tableColumns = normalizedColumns.map((col) => ({\n key: col.render ? `__rendered_${col.key}` : col.key,\n title: col.label || col.key,\n }))\n\n // Error state\n if (error) {\n return (\n <Alert variant=\"error\" className={className}>\n <p className=\"font-medium\">Error</p>\n <p className=\"mt-1 text-sm\">{error}</p>\n </Alert>\n )\n }\n\n // Empty state\n if (data.length === 0) {\n return (\n <Alert variant=\"info\" className={className}>\n {emptyMessage}\n </Alert>\n )\n }\n\n // Render table using lite-ui Table component\n return (\n <Table\n columns={tableColumns}\n data={processedData}\n keyField={String(keyField)}\n hoverable\n responsive\n className={className}\n />\n )\n}\n","/**\n * Billing API Client\n *\n * Handles all API calls for the Billing component.\n */\n\nimport type {\n BillingData,\n BillingProps,\n Customer,\n Product,\n Subscription,\n Invoice,\n PaymentMethod,\n ProductFeature\n} from './types';\n\ninterface ApiResponse<T> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\ninterface BillingClientConfig {\n baseUrl: string;\n apiKey?: string;\n accessToken?: string;\n projectId: number;\n}\n\nfunction createHeaders(config: BillingClientConfig): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json'\n };\n\n if (config.accessToken) {\n headers['Authorization'] = `Bearer ${config.accessToken}`;\n }\n\n if (config.apiKey) {\n headers['X-API-Key'] = config.apiKey;\n }\n\n return headers;\n}\n\nasync function fetchAPI<T>(\n config: BillingClientConfig,\n url: string,\n options: RequestInit = {}\n): Promise<ApiResponse<T>> {\n try {\n const response = await fetch(url, {\n ...options,\n headers: {\n ...createHeaders(config),\n ...options.headers as Record<string, string>,\n },\n // Prevent infinite re-fetching in Next.js\n cache: 'no-store',\n });\n\n // Check for non-OK responses before parsing\n if (!response.ok) {\n // Try to get error message from JSON, fallback to status text\n try {\n const errorData = await response.json();\n return {\n success: false,\n error: errorData.error || `HTTP ${response.status}: ${response.statusText}`\n };\n } catch {\n return {\n success: false,\n error: `HTTP ${response.status}: ${response.statusText}`\n };\n }\n }\n\n // Check content type to avoid parsing HTML as JSON\n const contentType = response.headers.get('content-type');\n if (!contentType || !contentType.includes('application/json')) {\n return {\n success: false,\n error: 'Invalid response: expected JSON'\n };\n }\n\n const data = await response.json();\n return data;\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'API request failed'\n };\n }\n}\n\n/**\n * Get or create customer for a user\n */\nasync function getOrCreateCustomer(\n config: BillingClientConfig,\n userId: string,\n organizationId: number,\n email: string,\n name?: string,\n testMode: boolean = true\n): Promise<ApiResponse<Customer>> {\n const { baseUrl, projectId } = config;\n\n // Try to get existing customer\n const customerData = await fetchAPI<Customer>(\n config,\n `${baseUrl}/billing/customers/by-user/${userId}?project_id=${projectId}`\n );\n\n if (customerData.success && customerData.data) {\n return customerData;\n }\n\n // Create new customer\n const createData = await fetchAPI<Customer>(\n config,\n `${baseUrl}/billing/customers`,\n {\n method: 'POST',\n body: JSON.stringify({\n project_id: projectId,\n user_id: userId,\n organization_id: organizationId,\n email,\n name,\n test_mode: testMode,\n sync_to_stripe: true\n })\n }\n );\n\n return createData;\n}\n\ninterface UserData {\n id: number;\n uuid?: string;\n email: string;\n full_name?: string;\n display_name?: string;\n organization_id?: number;\n}\n\n/**\n * Fetch user data by user ID\n */\nasync function fetchUserData(\n config: BillingClientConfig,\n userId: string\n): Promise<ApiResponse<UserData>> {\n const { baseUrl } = config;\n\n // Use the data/users endpoint (same as DataList uses)\n return fetchAPI<UserData>(\n config,\n `${baseUrl}/data/users/${userId}`\n );\n}\n\n/**\n * Fetch all billing data for a customer\n */\nexport async function fetchBillingData(\n props: BillingProps\n): Promise<ApiResponse<BillingData>> {\n const {\n baseUrl,\n apiKey,\n accessToken,\n projectId,\n userId,\n organizationId,\n userEmail,\n userName,\n testMode = true\n } = props;\n\n const config: BillingClientConfig = { baseUrl, apiKey, accessToken, projectId };\n\n try {\n // If email not provided, fetch user data first\n let email = userEmail;\n let name = userName;\n let orgId = organizationId;\n\n if (!email) {\n const userResult = await fetchUserData(config, userId);\n if (userResult.success && userResult.data) {\n email = userResult.data.email;\n name = name || userResult.data.full_name || userResult.data.display_name;\n orgId = orgId || userResult.data.organization_id;\n } else {\n return { success: false, error: userResult.error || 'User not found. Please provide userEmail.' };\n }\n }\n\n // 1. Get or create customer\n const finalOrgId = orgId || 1; // Default org if not provided\n const customerResult = await getOrCreateCustomer(\n config,\n userId,\n finalOrgId,\n email,\n name,\n testMode\n );\n\n if (!customerResult.success || !customerResult.data) {\n return { success: false, error: customerResult.error || 'Failed to get customer' };\n }\n\n const customer = customerResult.data;\n const customerId = customer.id;\n\n // 2. Fetch all billing data in parallel\n const [\n subsData,\n productsData,\n pricesData,\n entitlementsData,\n invoicesData,\n pmData,\n usageData\n ] = await Promise.all([\n fetchAPI<Subscription[]>(config, `${baseUrl}/billing/subscriptions?project_id=${projectId}&customer_id=${customerId}&status=active&include_items=true`),\n fetchAPI<Product[]>(config, `${baseUrl}/billing/products?project_id=${projectId}&limit=100&include_prices=false`),\n fetchAPI<any[]>(config, `${baseUrl}/billing/products/prices?project_id=${projectId}&limit=1000`),\n fetchAPI<any[]>(config, `${baseUrl}/billing/entitlements/list-entitlements?project_id=${projectId}&customer_id=${customerId}`),\n fetchAPI<Invoice[]>(config, `${baseUrl}/billing/invoices?project_id=${projectId}&customer_id=${customerId}&limit=10&sort=due_date:desc&include_line_items=true`),\n fetchAPI<PaymentMethod[]>(config, `${baseUrl}/billing/customers/${customerId}/payment-methods?project_id=${projectId}&status=active`),\n fetchAPI<Record<string, any>>(config, `${baseUrl}/billing/usage/get-all?project_id=${projectId}&customer_id=${customerId}`)\n ]);\n\n const currentSubscription = subsData.success && subsData.data && subsData.data.length > 0\n ? subsData.data[0]\n : null;\n const products = productsData.success && productsData.data ? productsData.data : [];\n const allPrices = pricesData.success && pricesData.data ? pricesData.data : [];\n const entitlements = entitlementsData.success && entitlementsData.data ? entitlementsData.data : [];\n const invoices = invoicesData.success && invoicesData.data ? invoicesData.data : [];\n const paymentMethods = pmData.success && pmData.data ? pmData.data : [];\n const featureUsage = usageData.success && usageData.data ? usageData.data : {};\n\n // 3. Group prices by product\n const pricesByProduct: Record<number, any[]> = {};\n allPrices.forEach((price: any) => {\n if (!pricesByProduct[price.product_id]) {\n pricesByProduct[price.product_id] = [];\n }\n pricesByProduct[price.product_id].push(price);\n });\n\n products.forEach((product: Product) => {\n product.prices = pricesByProduct[product.id] || [];\n });\n\n // 4. Fetch product features for all products\n const productFeatures: Record<number, ProductFeature[]> = {};\n await Promise.all(\n products.map(async (product: Product) => {\n const featuresData = await fetchAPI<ProductFeature[]>(\n config,\n `${baseUrl}/billing/features/product/${product.id}?project_id=${projectId}&limit=100`\n );\n productFeatures[product.id] = featuresData.success && featuresData.data ? featuresData.data : [];\n })\n );\n\n const billingData: BillingData = {\n customer,\n currentSubscription,\n products,\n productFeatures,\n entitlements,\n invoices,\n paymentMethods,\n featureUsage\n };\n\n return { success: true, data: billingData };\n } catch (error) {\n console.error('Error fetching billing data:', error);\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to fetch billing data'\n };\n }\n}\n\n/**\n * Fetch payment configuration (Stripe publishable key)\n */\nexport async function fetchPaymentConfig(\n config: BillingClientConfig,\n testMode: boolean = true\n): Promise<ApiResponse<{ publishable_key: string }>> {\n const { baseUrl, projectId } = config;\n\n return fetchAPI(\n config,\n `${baseUrl}/billing/payment-config?project_id=${projectId}&test_mode=${testMode}`\n );\n}\n\n/**\n * Tax calculation result\n */\nexport interface TaxInfo {\n tax_rate: number;\n tax_amount: number;\n tax_type: string;\n tax_label: string;\n jurisdiction: string;\n reverse_charge: boolean;\n customer_country?: string;\n seller_country?: string;\n subtotal: number;\n total: number;\n reason?: string;\n}\n\n/**\n * Fetch tax info for a customer\n * Uses a reference amount (e.g., $100) to get the tax rate\n */\nexport async function fetchTaxInfo(\n config: BillingClientConfig,\n customerId: number,\n referenceAmount: number = 10000 // $100 in cents\n): Promise<ApiResponse<TaxInfo>> {\n const { baseUrl, projectId } = config;\n\n return fetchAPI<TaxInfo>(\n config,\n `${baseUrl}/billing/tax/calculate`,\n {\n method: 'POST',\n body: JSON.stringify({\n project_id: projectId,\n customer_id: customerId,\n amount: referenceAmount,\n product_type: 'digital'\n })\n }\n );\n}\n","/**\n * Billing Component\n *\n * A server-side React component that displays billing information for a SaaS application.\n * Fetches real data from OmniKit Billing API.\n *\n * User email and name are automatically fetched from user data if not provided.\n *\n * @example\n * ```tsx\n * import { Billing } from '@omnikit-js/ui'\n *\n * export default async function BillingPage() {\n * return (\n * <Billing\n * baseUrl={process.env.OMNIKIT_BASE_URL!}\n * apiKey={process.env.OMNIKIT_API_KEY}\n * projectId={1}\n * userId=\"user_123\"\n * />\n * )\n * }\n * ```\n */\n\nimport { Alert } from '@marcoschwartz/lite-ui'\nimport { BillingContent } from '../../client/BillingContent'\nimport { fetchBillingData, fetchTaxInfo, type TaxInfo } from './client'\nimport type { BillingProps } from './types'\n\nexport type { BillingProps } from './types'\nexport type {\n BillingData,\n Customer,\n Subscription,\n Product,\n Price,\n Invoice,\n PaymentMethod,\n Entitlement,\n ProductFeature\n} from './types'\n\nexport async function Billing(props: BillingProps) {\n const {\n className = '',\n errorMessage = 'Failed to load billing information'\n } = props;\n\n // Fetch billing data from API\n const result = await fetchBillingData(props);\n\n // Error state\n if (!result.success || !result.data) {\n return (\n <Alert variant=\"error\" className={className}>\n <p className=\"font-medium\">Error</p>\n <p className=\"mt-1 text-sm\">{result.error || errorMessage}</p>\n </Alert>\n );\n }\n\n const { data } = result;\n\n // Fetch tax info for the customer (server-side)\n let initialTaxInfo: TaxInfo | null = null;\n if (data.customer?.id) {\n const taxConfig = {\n baseUrl: props.baseUrl,\n apiKey: props.apiKey,\n accessToken: props.accessToken,\n projectId: props.projectId,\n };\n const taxResult = await fetchTaxInfo(taxConfig, data.customer.id, 10000); // $100 reference\n if (taxResult.success && taxResult.data) {\n initialTaxInfo = taxResult.data;\n }\n }\n\n // Find current plan from subscription (like apteva-app does)\n const currentSubscriptionPriceId = data.currentSubscription?.items?.[0]?.price_id;\n const currentPlan = currentSubscriptionPriceId\n ? data.products.find(p => p.prices?.some(price => price.id === currentSubscriptionPriceId))\n : null;\n const currentPrice = currentPlan?.prices?.find(p => p.id === currentSubscriptionPriceId);\n\n // Transform data for BillingContent\n const subscription = data.currentSubscription ? {\n id: data.currentSubscription.id,\n plan: currentPlan?.name || 'Unknown Plan',\n status: data.currentSubscription.status,\n price: formatCurrency(\n currentPrice?.unit_amount || currentPrice?.amount || 0,\n data.currentSubscription.currency\n ),\n period: currentPrice?.recurring_interval || 'month',\n nextBilling: formatDate(data.currentSubscription.current_period_end),\n cancelAtPeriodEnd: data.currentSubscription.cancel_at_period_end,\n } : null;\n\n const invoices = data.invoices.map(inv => ({\n id: inv.number || inv.uuid,\n date: formatDate(inv.due_date || inv.created_at),\n amount: formatCurrency(inv.total || inv.amount_due, inv.currency),\n status: inv.status,\n url: inv.pdf_url || '#',\n }));\n\n const plans = data.products\n .filter(p => p.active && p.prices && p.prices.length > 0)\n .map(product => {\n const price = product.prices?.find(p => p.active) || product.prices?.[0];\n const features = data.productFeatures[product.id] || [];\n // Check if this product contains the current subscription's price\n const isCurrentPlan = currentSubscriptionPriceId\n ? product.prices?.some(p => p.id === currentSubscriptionPriceId)\n : false;\n\n // Get raw price amount for sorting\n const priceAmount = price?.unit_amount || 0;\n\n return {\n id: product.id,\n priceId: price?.id || 0,\n name: product.name,\n description: product.description,\n price: formatCurrency(priceAmount, price?.currency || 'usd'),\n priceAmount, // Raw amount in cents for sorting\n period: price?.recurring_interval || 'month',\n features: features.map(f => {\n // Get feature name and value\n const featureName = f.feature_name || f.feature_key || 'Feature';\n const featureValue = typeof f.value === 'object' && f.value !== null && 'value' in f.value\n ? f.value.value\n : f.value;\n const featureUnit = f.feature_unit || '';\n\n // If numeric feature with value, show it formatted\n if (featureValue !== undefined && featureValue !== null) {\n const formattedValue = typeof featureValue === 'number'\n ? featureValue.toLocaleString()\n : String(featureValue);\n return `${featureName}: ${formattedValue}${featureUnit ? ` ${featureUnit}` : ''}`.trim();\n }\n\n return featureName;\n }),\n current: isCurrentPlan || false,\n popular: product.name.toLowerCase().includes('pro') || product.name.toLowerCase().includes('professional'),\n };\n })\n .sort((a, b) => a.priceAmount - b.priceAmount); // Sort by price ascending (cheapest first)\n\n // Config for actions\n const actionConfig = {\n baseUrl: props.baseUrl,\n apiKey: props.apiKey,\n accessToken: props.accessToken,\n projectId: props.projectId,\n };\n\n return (\n <BillingContent\n subscription={subscription}\n invoices={invoices}\n paymentMethods={data.paymentMethods}\n plans={plans}\n customer={data.customer}\n entitlements={data.entitlements}\n featureUsage={data.featureUsage}\n actionConfig={actionConfig}\n initialTaxInfo={initialTaxInfo}\n className={className}\n />\n );\n}\n\n// Helper functions\nfunction formatCurrency(amount: number, currency: string = 'usd'): string {\n const formatter = new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency.toUpperCase(),\n });\n return formatter.format(amount / 100); // Stripe amounts are in cents\n}\n\nfunction formatDate(dateString: string | undefined): string {\n if (!dateString) return 'N/A';\n const date = new Date(dateString);\n return date.toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n });\n}\n"]}
|