@datatechsolutions/ui 2.11.86 → 2.11.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/billing-panel-DsHhhJqG.d.mts +18 -0
- package/dist/billing-panel-DsHhhJqG.d.ts +18 -0
- package/dist/chunk-4667D2ZT.mjs +61 -0
- package/dist/chunk-4667D2ZT.mjs.map +1 -0
- package/dist/chunk-5HXDJBVX.mjs +1330 -0
- package/dist/chunk-5HXDJBVX.mjs.map +1 -0
- package/dist/chunk-DJ33CSGJ.mjs +126 -0
- package/dist/chunk-DJ33CSGJ.mjs.map +1 -0
- package/dist/chunk-F4TOOARV.mjs +503 -0
- package/dist/chunk-F4TOOARV.mjs.map +1 -0
- package/dist/chunk-GEUGFYLO.mjs +237 -0
- package/dist/chunk-GEUGFYLO.mjs.map +1 -0
- package/dist/chunk-LBALE4JX.js +1342 -0
- package/dist/chunk-LBALE4JX.js.map +1 -0
- package/dist/chunk-MXFEU7A6.js +148 -0
- package/dist/chunk-MXFEU7A6.js.map +1 -0
- package/dist/chunk-NBCOVUQP.mjs +142 -0
- package/dist/chunk-NBCOVUQP.mjs.map +1 -0
- package/dist/chunk-P4RVGMZL.js +128 -0
- package/dist/chunk-P4RVGMZL.js.map +1 -0
- package/dist/chunk-Q2MG7S2E.js +239 -0
- package/dist/chunk-Q2MG7S2E.js.map +1 -0
- package/dist/chunk-RV555OEO.mjs +1009 -0
- package/dist/chunk-RV555OEO.mjs.map +1 -0
- package/dist/chunk-SAYVWIMJ.js +63 -0
- package/dist/chunk-SAYVWIMJ.js.map +1 -0
- package/dist/chunk-SUHNSUMH.mjs +1021 -0
- package/dist/chunk-SUHNSUMH.mjs.map +1 -0
- package/dist/chunk-TOEMSC4P.mjs +99 -0
- package/dist/chunk-TOEMSC4P.mjs.map +1 -0
- package/dist/chunk-UUHV5KHF.js +505 -0
- package/dist/chunk-UUHV5KHF.js.map +1 -0
- package/dist/chunk-UVEPTYZC.js +101 -0
- package/dist/chunk-UVEPTYZC.js.map +1 -0
- package/dist/chunk-X2KCCQPL.js +1049 -0
- package/dist/chunk-X2KCCQPL.js.map +1 -0
- package/dist/chunk-ZARCUQA6.js +1015 -0
- package/dist/chunk-ZARCUQA6.js.map +1 -0
- package/dist/platform/admin/index.d.mts +17 -0
- package/dist/platform/admin/index.d.ts +17 -0
- package/dist/platform/admin/index.js +39 -0
- package/dist/platform/admin/index.js.map +1 -0
- package/dist/platform/admin/index.mjs +10 -0
- package/dist/platform/admin/index.mjs.map +1 -0
- package/dist/platform/auth/index.d.mts +73 -0
- package/dist/platform/auth/index.d.ts +73 -0
- package/dist/platform/auth/index.js +107 -0
- package/dist/platform/auth/index.js.map +1 -0
- package/dist/platform/auth/index.mjs +10 -0
- package/dist/platform/auth/index.mjs.map +1 -0
- package/dist/platform/billing/index.d.mts +29 -0
- package/dist/platform/billing/index.d.ts +29 -0
- package/dist/platform/billing/index.js +22 -0
- package/dist/platform/billing/index.js.map +1 -0
- package/dist/platform/billing/index.mjs +9 -0
- package/dist/platform/billing/index.mjs.map +1 -0
- package/dist/platform/impersonation/index.d.mts +19 -0
- package/dist/platform/impersonation/index.d.ts +19 -0
- package/dist/platform/impersonation/index.js +17 -0
- package/dist/platform/impersonation/index.js.map +1 -0
- package/dist/platform/impersonation/index.mjs +8 -0
- package/dist/platform/impersonation/index.mjs.map +1 -0
- package/dist/platform/index.d.mts +45 -2
- package/dist/platform/index.d.ts +45 -2
- package/dist/platform/index.js +4850 -0
- package/dist/platform/index.js.map +1 -1
- package/dist/platform/index.mjs +4716 -3
- package/dist/platform/index.mjs.map +1 -1
- package/dist/platform/settings/index.d.mts +31 -0
- package/dist/platform/settings/index.d.ts +31 -0
- package/dist/platform/settings/index.js +21 -0
- package/dist/platform/settings/index.js.map +1 -0
- package/dist/platform/settings/index.mjs +12 -0
- package/dist/platform/settings/index.mjs.map +1 -0
- package/package.json +26 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { InlineSpinner, SectionCard, StatusBadge, Badge, Button, ListCard, ListCardItem } from './chunk-ZJQ5RLGK.mjs';
|
|
3
|
+
import { triggerHaptic } from './chunk-D2JF6C3E.mjs';
|
|
4
|
+
import { useTranslations, useFormatter } from './chunk-7VJ7CMMT.mjs';
|
|
5
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
6
|
+
import { useAuth } from '@datatechsolutions/windsock/client';
|
|
7
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
var TIER_ORDER = {
|
|
10
|
+
free_trial: 0,
|
|
11
|
+
starter: 1,
|
|
12
|
+
professional: 2,
|
|
13
|
+
enterprise: 3
|
|
14
|
+
};
|
|
15
|
+
function BillingPanel({
|
|
16
|
+
defaultBillingInterval = "monthly",
|
|
17
|
+
onRedirect
|
|
18
|
+
}) {
|
|
19
|
+
const t = useTranslations("windsock");
|
|
20
|
+
const format = useFormatter();
|
|
21
|
+
const { client } = useAuth();
|
|
22
|
+
const [subscription, setSubscription] = useState(null);
|
|
23
|
+
const [plans, setPlans] = useState([]);
|
|
24
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
25
|
+
const [error, setError] = useState(null);
|
|
26
|
+
const [busyAction, setBusyAction] = useState(null);
|
|
27
|
+
const redirect = useCallback((url) => {
|
|
28
|
+
if (onRedirect) {
|
|
29
|
+
onRedirect(url);
|
|
30
|
+
} else if (typeof window !== "undefined") {
|
|
31
|
+
window.location.assign(url);
|
|
32
|
+
}
|
|
33
|
+
}, [onRedirect]);
|
|
34
|
+
const load = useCallback(async () => {
|
|
35
|
+
setIsLoading(true);
|
|
36
|
+
setError(null);
|
|
37
|
+
try {
|
|
38
|
+
const [sub, availablePlans] = await Promise.all([
|
|
39
|
+
client.getSubscription(),
|
|
40
|
+
client.getPlans()
|
|
41
|
+
]);
|
|
42
|
+
setSubscription(sub);
|
|
43
|
+
setPlans(
|
|
44
|
+
[...availablePlans].filter((plan) => plan.active).sort((a, b) => (TIER_ORDER[a.tier] ?? 0) - (TIER_ORDER[b.tier] ?? 0))
|
|
45
|
+
);
|
|
46
|
+
} catch (loadError) {
|
|
47
|
+
setError(loadError instanceof Error ? loadError.message : String(loadError));
|
|
48
|
+
} finally {
|
|
49
|
+
setIsLoading(false);
|
|
50
|
+
}
|
|
51
|
+
}, [client]);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
void load();
|
|
54
|
+
}, [load]);
|
|
55
|
+
const handleOpenPortal = useCallback(async () => {
|
|
56
|
+
setBusyAction("portal");
|
|
57
|
+
triggerHaptic("light");
|
|
58
|
+
try {
|
|
59
|
+
const { url } = await client.createPortalSession();
|
|
60
|
+
redirect(url);
|
|
61
|
+
} catch (portalError) {
|
|
62
|
+
setError(portalError instanceof Error ? portalError.message : String(portalError));
|
|
63
|
+
triggerHaptic("error");
|
|
64
|
+
} finally {
|
|
65
|
+
setBusyAction(null);
|
|
66
|
+
}
|
|
67
|
+
}, [client, redirect]);
|
|
68
|
+
const handleStartCheckout = useCallback(async (planCode) => {
|
|
69
|
+
setBusyAction(`checkout:${planCode}`);
|
|
70
|
+
triggerHaptic("light");
|
|
71
|
+
try {
|
|
72
|
+
const { url } = await client.createCheckoutSession(planCode, defaultBillingInterval);
|
|
73
|
+
redirect(url);
|
|
74
|
+
} catch (checkoutError) {
|
|
75
|
+
setError(checkoutError instanceof Error ? checkoutError.message : String(checkoutError));
|
|
76
|
+
triggerHaptic("error");
|
|
77
|
+
} finally {
|
|
78
|
+
setBusyAction(null);
|
|
79
|
+
}
|
|
80
|
+
}, [client, defaultBillingInterval, redirect]);
|
|
81
|
+
const handleCancel = useCallback(async () => {
|
|
82
|
+
if (typeof window !== "undefined") {
|
|
83
|
+
const confirmed = window.confirm(t("billing.cancelConfirm"));
|
|
84
|
+
if (!confirmed) return;
|
|
85
|
+
}
|
|
86
|
+
setBusyAction("cancel");
|
|
87
|
+
triggerHaptic("warning");
|
|
88
|
+
try {
|
|
89
|
+
const result = await client.cancelSubscription();
|
|
90
|
+
if (!result.success) {
|
|
91
|
+
throw new Error(result.error ?? "Subscription cancellation failed");
|
|
92
|
+
}
|
|
93
|
+
triggerHaptic("success");
|
|
94
|
+
await load();
|
|
95
|
+
} catch (cancelError) {
|
|
96
|
+
setError(cancelError instanceof Error ? cancelError.message : String(cancelError));
|
|
97
|
+
triggerHaptic("error");
|
|
98
|
+
} finally {
|
|
99
|
+
setBusyAction(null);
|
|
100
|
+
}
|
|
101
|
+
}, [client, load, t]);
|
|
102
|
+
if (isLoading) {
|
|
103
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(InlineSpinner, {}) });
|
|
104
|
+
}
|
|
105
|
+
const currentPlan = subscription?.plan ?? plans.find((plan) => plan.id === subscription?.planId) ?? null;
|
|
106
|
+
const currentTier = subscription?.tier ?? currentPlan?.tier ?? null;
|
|
107
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
108
|
+
error && /* @__PURE__ */ jsx(
|
|
109
|
+
"div",
|
|
110
|
+
{
|
|
111
|
+
role: "alert",
|
|
112
|
+
className: "rounded-xl border border-red-300/40 bg-red-50/80 px-4 py-3 text-sm text-red-700 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-300",
|
|
113
|
+
children: error
|
|
114
|
+
}
|
|
115
|
+
),
|
|
116
|
+
/* @__PURE__ */ jsx(
|
|
117
|
+
SectionCard,
|
|
118
|
+
{
|
|
119
|
+
header: {
|
|
120
|
+
title: t("billing.currentPlan.title"),
|
|
121
|
+
subtitle: t("billing.currentPlan.description")
|
|
122
|
+
},
|
|
123
|
+
children: subscription && currentPlan ? /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
124
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
125
|
+
/* @__PURE__ */ jsx("span", { className: "text-lg font-semibold text-gray-900 dark:text-white", children: currentPlan.name }),
|
|
126
|
+
/* @__PURE__ */ jsx(
|
|
127
|
+
StatusBadge,
|
|
128
|
+
{
|
|
129
|
+
status: subscription.status === "active" ? "success" : subscription.status === "past_due" ? "pending" : "error",
|
|
130
|
+
label: t(`billing.status.${subscription.status}`)
|
|
131
|
+
}
|
|
132
|
+
),
|
|
133
|
+
subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsx(Badge, { color: "amber", children: t("billing.currentPlan.cancelAtPeriodEnd") })
|
|
134
|
+
] }),
|
|
135
|
+
/* @__PURE__ */ jsxs("dl", { className: "grid grid-cols-1 gap-3 text-sm sm:grid-cols-2", children: [
|
|
136
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
137
|
+
/* @__PURE__ */ jsx("dt", { className: "text-gray-500 dark:text-gray-400", children: t("billing.currentPlan.price") }),
|
|
138
|
+
/* @__PURE__ */ jsxs("dd", { className: "font-medium text-gray-900 dark:text-white", children: [
|
|
139
|
+
format.number(currentPlan.price, { style: "currency", currency: currentPlan.currency }),
|
|
140
|
+
" ",
|
|
141
|
+
"/",
|
|
142
|
+
" ",
|
|
143
|
+
t(`billing.interval.${currentPlan.interval}`)
|
|
144
|
+
] })
|
|
145
|
+
] }),
|
|
146
|
+
subscription.currentPeriodEnd && /* @__PURE__ */ jsxs("div", { children: [
|
|
147
|
+
/* @__PURE__ */ jsx("dt", { className: "text-gray-500 dark:text-gray-400", children: t("billing.currentPlan.renewsOn") }),
|
|
148
|
+
/* @__PURE__ */ jsx("dd", { className: "font-medium text-gray-900 dark:text-white", children: format.dateTime(new Date(subscription.currentPeriodEnd), { dateStyle: "medium" }) })
|
|
149
|
+
] })
|
|
150
|
+
] }),
|
|
151
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 pt-2", children: [
|
|
152
|
+
/* @__PURE__ */ jsx(
|
|
153
|
+
Button,
|
|
154
|
+
{
|
|
155
|
+
size: "sm",
|
|
156
|
+
color: "ios-glass-blue",
|
|
157
|
+
onClick: handleOpenPortal,
|
|
158
|
+
loading: busyAction === "portal",
|
|
159
|
+
disabled: busyAction !== null,
|
|
160
|
+
children: t("billing.currentPlan.manageInPortal")
|
|
161
|
+
}
|
|
162
|
+
),
|
|
163
|
+
subscription.status === "active" && !subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsx(
|
|
164
|
+
Button,
|
|
165
|
+
{
|
|
166
|
+
size: "sm",
|
|
167
|
+
color: "ios-glass-red",
|
|
168
|
+
onClick: handleCancel,
|
|
169
|
+
loading: busyAction === "cancel",
|
|
170
|
+
disabled: busyAction !== null,
|
|
171
|
+
children: t("billing.currentPlan.cancel")
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
] })
|
|
175
|
+
] }) : /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: t("billing.currentPlan.noSubscription") })
|
|
176
|
+
}
|
|
177
|
+
),
|
|
178
|
+
/* @__PURE__ */ jsx(
|
|
179
|
+
SectionCard,
|
|
180
|
+
{
|
|
181
|
+
header: {
|
|
182
|
+
title: t("billing.plans.title"),
|
|
183
|
+
subtitle: t("billing.plans.description")
|
|
184
|
+
},
|
|
185
|
+
children: plans.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: t("billing.plans.empty") }) : /* @__PURE__ */ jsx(ListCard, { children: plans.map((plan) => {
|
|
186
|
+
const isCurrent = currentPlan?.id === plan.id;
|
|
187
|
+
const isDowngrade = currentTier != null && (TIER_ORDER[plan.tier] ?? 0) < (TIER_ORDER[currentTier] ?? 0);
|
|
188
|
+
return /* @__PURE__ */ jsx(
|
|
189
|
+
ListCardItem,
|
|
190
|
+
{
|
|
191
|
+
leading: /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-violet-500/20 to-blue-500/20 text-violet-600 dark:text-violet-300", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-bold uppercase", children: plan.tier.charAt(0) }) }),
|
|
192
|
+
trailing: isCurrent ? /* @__PURE__ */ jsx(Badge, { color: "green", children: t("billing.plans.currentBadge") }) : isDowngrade ? /* @__PURE__ */ jsx(
|
|
193
|
+
Button,
|
|
194
|
+
{
|
|
195
|
+
size: "sm",
|
|
196
|
+
plain: true,
|
|
197
|
+
onClick: () => handleStartCheckout(plan.code),
|
|
198
|
+
loading: busyAction === `checkout:${plan.code}`,
|
|
199
|
+
disabled: busyAction !== null,
|
|
200
|
+
children: t("billing.plans.downgradeAction")
|
|
201
|
+
}
|
|
202
|
+
) : /* @__PURE__ */ jsx(
|
|
203
|
+
Button,
|
|
204
|
+
{
|
|
205
|
+
size: "sm",
|
|
206
|
+
color: "ios-glass-blue",
|
|
207
|
+
onClick: () => handleStartCheckout(plan.code),
|
|
208
|
+
loading: busyAction === `checkout:${plan.code}`,
|
|
209
|
+
disabled: busyAction !== null,
|
|
210
|
+
children: t("billing.plans.selectAction")
|
|
211
|
+
}
|
|
212
|
+
),
|
|
213
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
214
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
215
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-white", children: plan.name }),
|
|
216
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
|
|
217
|
+
format.number(plan.price, { style: "currency", currency: plan.currency }),
|
|
218
|
+
" ",
|
|
219
|
+
"/",
|
|
220
|
+
" ",
|
|
221
|
+
t(`billing.interval.${plan.interval}`)
|
|
222
|
+
] })
|
|
223
|
+
] }),
|
|
224
|
+
plan.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: plan.description })
|
|
225
|
+
] })
|
|
226
|
+
},
|
|
227
|
+
plan.id
|
|
228
|
+
);
|
|
229
|
+
}) })
|
|
230
|
+
}
|
|
231
|
+
)
|
|
232
|
+
] });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export { BillingPanel };
|
|
236
|
+
//# sourceMappingURL=chunk-GEUGFYLO.mjs.map
|
|
237
|
+
//# sourceMappingURL=chunk-GEUGFYLO.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/platform/billing/billing-panel.tsx"],"names":[],"mappings":";;;;;;;AAyCA,IAAM,UAAA,GAA+C;AAAA,EACnD,UAAA,EAAY,CAAA;AAAA,EACZ,OAAA,EAAS,CAAA;AAAA,EACT,YAAA,EAAc,CAAA;AAAA,EACd,UAAA,EAAY;AACd,CAAA;AAEO,SAAS,YAAA,CAAa;AAAA,EAC3B,sBAAA,GAAyB,SAAA;AAAA,EACzB;AACF,CAAA,EAAsB;AACpB,EAAA,MAAM,CAAA,GAAI,gBAAgB,UAAU,CAAA;AACpC,EAAA,MAAM,SAAS,YAAA,EAAa;AAC5B,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,OAAA,EAAQ;AAE3B,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAqC,IAAI,CAAA;AACjF,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA,CAA2B,EAAE,CAAA;AACvD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,IAAI,CAAA;AAC/C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAwB,IAAI,CAAA;AACtD,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAwB,IAAI,CAAA;AAEhE,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,CAAC,GAAA,KAAgB;AAC5C,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,UAAA,CAAW,GAAG,CAAA;AAAA,IAChB,CAAA,MAAA,IAAW,OAAO,MAAA,KAAW,WAAA,EAAa;AACxC,MAAA,MAAA,CAAO,QAAA,CAAS,OAAO,GAAG,CAAA;AAAA,IAC5B;AAAA,EACF,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,MAAM,IAAA,GAAO,YAAY,YAAY;AACnC,IAAA,YAAA,CAAa,IAAI,CAAA;AACjB,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,IAAI;AACF,MAAA,MAAM,CAAC,GAAA,EAAK,cAAc,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,QAC9C,OAAO,eAAA,EAAgB;AAAA,QACvB,OAAO,QAAA;AAAS,OACjB,CAAA;AACD,MAAA,eAAA,CAAgB,GAAG,CAAA;AACnB,MAAA,QAAA;AAAA,QACE,CAAC,GAAG,cAAc,CAAA,CACf,OAAO,CAAC,IAAA,KAAS,IAAA,CAAK,MAAM,CAAA,CAC5B,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAA,CAAO,UAAA,CAAW,CAAA,CAAE,IAAI,CAAA,IAAK,MAAM,UAAA,CAAW,CAAA,CAAE,IAAI,CAAA,IAAK,CAAA,CAAE;AAAA,OACzE;AAAA,IACF,SAAS,SAAA,EAAW;AAClB,MAAA,QAAA,CAAS,qBAAqB,KAAA,GAAQ,SAAA,CAAU,OAAA,GAAU,MAAA,CAAO,SAAS,CAAC,CAAA;AAAA,IAC7E,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,KAAK,IAAA,EAAK;AAAA,EACZ,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,MAAM,gBAAA,GAAmB,YAAY,YAAY;AAC/C,IAAA,aAAA,CAAc,QAAQ,CAAA;AACtB,IAAA,aAAA,CAAc,OAAO,CAAA;AACrB,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,GAAA,EAAI,GAAI,MAAM,OAAO,mBAAA,EAAoB;AACjD,MAAA,QAAA,CAAS,GAAG,CAAA;AAAA,IACd,SAAS,WAAA,EAAa;AACpB,MAAA,QAAA,CAAS,uBAAuB,KAAA,GAAQ,WAAA,CAAY,OAAA,GAAU,MAAA,CAAO,WAAW,CAAC,CAAA;AACjF,MAAA,aAAA,CAAc,OAAO,CAAA;AAAA,IACvB,CAAA,SAAE;AACA,MAAA,aAAA,CAAc,IAAI,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,QAAQ,CAAC,CAAA;AAErB,EAAA,MAAM,mBAAA,GAAsB,WAAA,CAAY,OAAO,QAAA,KAAqB;AAClE,IAAA,aAAA,CAAc,CAAA,SAAA,EAAY,QAAQ,CAAA,CAAE,CAAA;AACpC,IAAA,aAAA,CAAc,OAAO,CAAA;AACrB,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,GAAA,EAAI,GAAI,MAAM,MAAA,CAAO,qBAAA,CAAsB,UAAU,sBAAsB,CAAA;AACnF,MAAA,QAAA,CAAS,GAAG,CAAA;AAAA,IACd,SAAS,aAAA,EAAe;AACtB,MAAA,QAAA,CAAS,yBAAyB,KAAA,GAAQ,aAAA,CAAc,OAAA,GAAU,MAAA,CAAO,aAAa,CAAC,CAAA;AACvF,MAAA,aAAA,CAAc,OAAO,CAAA;AAAA,IACvB,CAAA,SAAE;AACA,MAAA,aAAA,CAAc,IAAI,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,sBAAA,EAAwB,QAAQ,CAAC,CAAA;AAE7C,EAAA,MAAM,YAAA,GAAe,YAAY,YAAY;AAC3C,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,MAAA,MAAM,SAAA,GAAY,MAAA,CAAO,OAAA,CAAQ,CAAA,CAAE,uBAAuB,CAAC,CAAA;AAC3D,MAAA,IAAI,CAAC,SAAA,EAAW;AAAA,IAClB;AACA,IAAA,aAAA,CAAc,QAAQ,CAAA;AACtB,IAAA,aAAA,CAAc,SAAS,CAAA;AACvB,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,kBAAA,EAAmB;AAC/C,MAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,QAAA,MAAM,IAAI,KAAA,CAAM,MAAA,CAAO,KAAA,IAAS,kCAAkC,CAAA;AAAA,MACpE;AACA,MAAA,aAAA,CAAc,SAAS,CAAA;AACvB,MAAA,MAAM,IAAA,EAAK;AAAA,IACb,SAAS,WAAA,EAAa;AACpB,MAAA,QAAA,CAAS,uBAAuB,KAAA,GAAQ,WAAA,CAAY,OAAA,GAAU,MAAA,CAAO,WAAW,CAAC,CAAA;AACjF,MAAA,aAAA,CAAc,OAAO,CAAA;AAAA,IACvB,CAAA,SAAE;AACA,MAAA,aAAA,CAAc,IAAI,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,IAAA,EAAM,CAAC,CAAC,CAAA;AAEpB,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,2BACG,KAAA,EAAA,EAAI,SAAA,EAAU,wCAAA,EACb,QAAA,kBAAA,GAAA,CAAC,iBAAc,CAAA,EACjB,CAAA;AAAA,EAEJ;AAEA,EAAA,MAAM,WAAA,GAAc,YAAA,EAAc,IAAA,IAAQ,KAAA,CAAM,IAAA,CAAK,CAAC,IAAA,KAAS,IAAA,CAAK,EAAA,KAAO,YAAA,EAAc,MAAM,CAAA,IAAK,IAAA;AACpG,EAAA,MAAM,WAAA,GAAc,YAAA,EAAc,IAAA,IAAQ,WAAA,EAAa,IAAA,IAAQ,IAAA;AAE/D,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,WAAA,EACZ,QAAA,EAAA;AAAA,IAAA,KAAA,oBACC,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,OAAA;AAAA,QACL,SAAA,EAAU,6IAAA;AAAA,QAET,QAAA,EAAA;AAAA;AAAA,KACH;AAAA,oBAGF,GAAA;AAAA,MAAC,WAAA;AAAA,MAAA;AAAA,QACC,MAAA,EAAQ;AAAA,UACN,KAAA,EAAO,EAAE,2BAA2B,CAAA;AAAA,UACpC,QAAA,EAAU,EAAE,iCAAiC;AAAA,SAC/C;AAAA,QAEC,QAAA,EAAA,YAAA,IAAgB,WAAA,mBACf,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,WAAA,EACb,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,mCAAA,EACb,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,qDAAA,EACb,QAAA,EAAA,WAAA,CAAY,IAAA,EACf,CAAA;AAAA,4BACA,GAAA;AAAA,cAAC,WAAA;AAAA,cAAA;AAAA,gBACC,MAAA,EACE,aAAa,MAAA,KAAW,QAAA,GAAW,YACjC,YAAA,CAAa,MAAA,KAAW,aAAa,SAAA,GACrC,OAAA;AAAA,gBAEJ,KAAA,EAAO,CAAA,CAAE,CAAA,eAAA,EAAkB,YAAA,CAAa,MAAM,CAAA,CAAE;AAAA;AAAA,aAClD;AAAA,YACC,YAAA,CAAa,qCACZ,GAAA,CAAC,KAAA,EAAA,EAAM,OAAM,OAAA,EAAS,QAAA,EAAA,CAAA,CAAE,uCAAuC,CAAA,EAAE;AAAA,WAAA,EAErE,CAAA;AAAA,0BAEA,IAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,+CAAA,EACZ,QAAA,EAAA;AAAA,4BAAA,IAAA,CAAC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,kCAAA,EACX,QAAA,EAAA,CAAA,CAAE,2BAA2B,CAAA,EAChC,CAAA;AAAA,8BACA,IAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,2CAAA,EACX,QAAA,EAAA;AAAA,gBAAA,MAAA,CAAO,MAAA,CAAO,YAAY,KAAA,EAAO,EAAE,OAAO,UAAA,EAAY,QAAA,EAAU,WAAA,CAAY,QAAA,EAAU,CAAA;AAAA,gBACtF,GAAA;AAAA,gBAAI,GAAA;AAAA,gBAAE,GAAA;AAAA,gBACN,CAAA,CAAE,CAAA,iBAAA,EAAoB,WAAA,CAAY,QAAQ,CAAA,CAAE;AAAA,eAAA,EAC/C;AAAA,aAAA,EACF,CAAA;AAAA,YACC,YAAA,CAAa,gBAAA,oBACZ,IAAA,CAAC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,kCAAA,EACX,QAAA,EAAA,CAAA,CAAE,8BAA8B,CAAA,EACnC,CAAA;AAAA,8BACA,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,2CAAA,EACX,iBAAO,QAAA,CAAS,IAAI,IAAA,CAAK,YAAA,CAAa,gBAAgB,CAAA,EAAG,EAAE,SAAA,EAAW,QAAA,EAAU,CAAA,EACnF;AAAA,aAAA,EACF;AAAA,WAAA,EAEJ,CAAA;AAAA,0BAEA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2BAAA,EACb,QAAA,EAAA;AAAA,4BAAA,GAAA;AAAA,cAAC,MAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,IAAA;AAAA,gBACL,KAAA,EAAM,gBAAA;AAAA,gBACN,OAAA,EAAS,gBAAA;AAAA,gBACT,SAAS,UAAA,KAAe,QAAA;AAAA,gBACxB,UAAU,UAAA,KAAe,IAAA;AAAA,gBAExB,YAAE,oCAAoC;AAAA;AAAA,aACzC;AAAA,YACC,YAAA,CAAa,MAAA,KAAW,QAAA,IAAY,CAAC,aAAa,iBAAA,oBACjD,GAAA;AAAA,cAAC,MAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,IAAA;AAAA,gBACL,KAAA,EAAM,eAAA;AAAA,gBACN,OAAA,EAAS,YAAA;AAAA,gBACT,SAAS,UAAA,KAAe,QAAA;AAAA,gBACxB,UAAU,UAAA,KAAe,IAAA;AAAA,gBAExB,YAAE,4BAA4B;AAAA;AAAA;AACjC,WAAA,EAEJ;AAAA,SAAA,EACF,oBAEA,GAAA,CAAC,GAAA,EAAA,EAAE,WAAU,0CAAA,EACV,QAAA,EAAA,CAAA,CAAE,oCAAoC,CAAA,EACzC;AAAA;AAAA,KAEJ;AAAA,oBAEA,GAAA;AAAA,MAAC,WAAA;AAAA,MAAA;AAAA,QACC,MAAA,EAAQ;AAAA,UACN,KAAA,EAAO,EAAE,qBAAqB,CAAA;AAAA,UAC9B,QAAA,EAAU,EAAE,2BAA2B;AAAA,SACzC;AAAA,QAEC,gBAAM,MAAA,KAAW,CAAA,mBAChB,GAAA,CAAC,GAAA,EAAA,EAAE,WAAU,0CAAA,EACV,QAAA,EAAA,CAAA,CAAE,qBAAqB,CAAA,EAC1B,oBAEA,GAAA,CAAC,QAAA,EAAA,EACE,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACnB,UAAA,MAAM,SAAA,GAAY,WAAA,EAAa,EAAA,KAAO,IAAA,CAAK,EAAA;AAC3C,UAAA,MAAM,WAAA,GAAc,WAAA,IAAe,IAAA,IAAA,CAC7B,UAAA,CAAW,IAAA,CAAK,IAAI,CAAA,IAAK,CAAA,KAAM,UAAA,CAAW,WAAW,CAAA,IAAK,CAAA,CAAA;AAChE,UAAA,uBACE,GAAA;AAAA,YAAC,YAAA;AAAA,YAAA;AAAA,cAEC,OAAA,kBACE,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,kJACb,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,6BAAA,EAA+B,QAAA,EAAA,IAAA,CAAK,IAAA,CAAK,MAAA,CAAO,CAAC,GAAE,CAAA,EACrE,CAAA;AAAA,cAEF,QAAA,EACE,SAAA,mBACE,GAAA,CAAC,KAAA,EAAA,EAAM,KAAA,EAAM,SAAS,QAAA,EAAA,CAAA,CAAE,4BAA4B,CAAA,EAAE,CAAA,GAEtD,WAAA,mBACE,GAAA;AAAA,gBAAC,MAAA;AAAA,gBAAA;AAAA,kBACC,IAAA,EAAK,IAAA;AAAA,kBACL,KAAA,EAAK,IAAA;AAAA,kBACL,OAAA,EAAS,MAAM,mBAAA,CAAoB,IAAA,CAAK,IAAI,CAAA;AAAA,kBAC5C,OAAA,EAAS,UAAA,KAAe,CAAA,SAAA,EAAY,IAAA,CAAK,IAAI,CAAA,CAAA;AAAA,kBAC7C,UAAU,UAAA,KAAe,IAAA;AAAA,kBAExB,YAAE,+BAA+B;AAAA;AAAA,eACpC,mBAEA,GAAA;AAAA,gBAAC,MAAA;AAAA,gBAAA;AAAA,kBACC,IAAA,EAAK,IAAA;AAAA,kBACL,KAAA,EAAM,gBAAA;AAAA,kBACN,OAAA,EAAS,MAAM,mBAAA,CAAoB,IAAA,CAAK,IAAI,CAAA;AAAA,kBAC5C,OAAA,EAAS,UAAA,KAAe,CAAA,SAAA,EAAY,IAAA,CAAK,IAAI,CAAA,CAAA;AAAA,kBAC7C,UAAU,UAAA,KAAe,IAAA;AAAA,kBAExB,YAAE,4BAA4B;AAAA;AAAA,eACjC;AAAA,cAKN,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qBAAA,EACb,QAAA,EAAA;AAAA,gCAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,yBAAA,EACb,QAAA,EAAA;AAAA,kCAAA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,mDAAA,EACV,QAAA,EAAA,IAAA,CAAK,IAAA,EACR,CAAA;AAAA,kCACA,IAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,0CAAA,EACb,QAAA,EAAA;AAAA,oBAAA,MAAA,CAAO,MAAA,CAAO,KAAK,KAAA,EAAO,EAAE,OAAO,UAAA,EAAY,QAAA,EAAU,IAAA,CAAK,QAAA,EAAU,CAAA;AAAA,oBACxE,GAAA;AAAA,oBAAI,GAAA;AAAA,oBAAE,GAAA;AAAA,oBACN,CAAA,CAAE,CAAA,iBAAA,EAAoB,IAAA,CAAK,QAAQ,CAAA,CAAE;AAAA,mBAAA,EACxC;AAAA,iBAAA,EACF,CAAA;AAAA,gBACC,KAAK,WAAA,oBACJ,GAAA,CAAC,OAAE,SAAA,EAAU,0CAAA,EACV,eAAK,WAAA,EACR;AAAA,eAAA,EAEJ;AAAA,aAAA;AAAA,YAlDK,IAAA,CAAK;AAAA,WAmDZ;AAAA,QAEJ,CAAC,CAAA,EACH;AAAA;AAAA;AAEJ,GAAA,EACF,CAAA;AAEJ","file":"chunk-GEUGFYLO.mjs","sourcesContent":["// =============================================================================\n// @datatechsolutions/windsock/ui — BillingPanel\n// Shows the current organization subscription plus the plan catalogue, with\n// actions for opening the Stripe portal, starting checkout, or cancelling.\n// Backed by `AuthClient.getSubscription / getPlans / createPortalSession /\n// createCheckoutSession / cancelSubscription`.\n// =============================================================================\n\nimport { useEffect, useState, useCallback } from 'react'\nimport { useTranslations, useFormatter } from '@ui/lib/i18n-context'\nimport {\n SectionCard,\n Button,\n Badge,\n StatusBadge,\n InlineSpinner,\n ListCard,\n ListCardItem,\n triggerHaptic,\n} from '@ui/index'\nimport type {\n SubscriptionDetails,\n PlanDefinition,\n SubscriptionTier,\n} from '@datatechsolutions/shared-domain'\nimport { useAuth } from '@datatechsolutions/windsock/client'\n\nexport interface BillingPanelProps {\n /**\n * Which billing interval to prefer when starting checkout. Defaults to\n * `'monthly'`. Surface a toggle in the host app if both matter.\n */\n defaultBillingInterval?: 'monthly' | 'yearly'\n /**\n * Called after the portal/checkout URL is resolved. Defaults to\n * `window.location.assign(url)`. Override to open in a new tab, use a\n * router, etc.\n */\n onRedirect?: (url: string) => void\n}\n\nconst TIER_ORDER: Record<SubscriptionTier, number> = {\n free_trial: 0,\n starter: 1,\n professional: 2,\n enterprise: 3,\n}\n\nexport function BillingPanel({\n defaultBillingInterval = 'monthly',\n onRedirect,\n}: BillingPanelProps) {\n const t = useTranslations('windsock')\n const format = useFormatter()\n const { client } = useAuth()\n\n const [subscription, setSubscription] = useState<SubscriptionDetails | null>(null)\n const [plans, setPlans] = useState<PlanDefinition[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const [busyAction, setBusyAction] = useState<string | null>(null)\n\n const redirect = useCallback((url: string) => {\n if (onRedirect) {\n onRedirect(url)\n } else if (typeof window !== 'undefined') {\n window.location.assign(url)\n }\n }, [onRedirect])\n\n const load = useCallback(async () => {\n setIsLoading(true)\n setError(null)\n try {\n const [sub, availablePlans] = await Promise.all([\n client.getSubscription(),\n client.getPlans(),\n ])\n setSubscription(sub)\n setPlans(\n [...availablePlans]\n .filter((plan) => plan.active)\n .sort((a, b) => (TIER_ORDER[a.tier] ?? 0) - (TIER_ORDER[b.tier] ?? 0)),\n )\n } catch (loadError) {\n setError(loadError instanceof Error ? loadError.message : String(loadError))\n } finally {\n setIsLoading(false)\n }\n }, [client])\n\n useEffect(() => {\n void load()\n }, [load])\n\n const handleOpenPortal = useCallback(async () => {\n setBusyAction('portal')\n triggerHaptic('light')\n try {\n const { url } = await client.createPortalSession()\n redirect(url)\n } catch (portalError) {\n setError(portalError instanceof Error ? portalError.message : String(portalError))\n triggerHaptic('error')\n } finally {\n setBusyAction(null)\n }\n }, [client, redirect])\n\n const handleStartCheckout = useCallback(async (planCode: string) => {\n setBusyAction(`checkout:${planCode}`)\n triggerHaptic('light')\n try {\n const { url } = await client.createCheckoutSession(planCode, defaultBillingInterval)\n redirect(url)\n } catch (checkoutError) {\n setError(checkoutError instanceof Error ? checkoutError.message : String(checkoutError))\n triggerHaptic('error')\n } finally {\n setBusyAction(null)\n }\n }, [client, defaultBillingInterval, redirect])\n\n const handleCancel = useCallback(async () => {\n if (typeof window !== 'undefined') {\n const confirmed = window.confirm(t('billing.cancelConfirm'))\n if (!confirmed) return\n }\n setBusyAction('cancel')\n triggerHaptic('warning')\n try {\n const result = await client.cancelSubscription()\n if (!result.success) {\n throw new Error(result.error ?? 'Subscription cancellation failed')\n }\n triggerHaptic('success')\n await load()\n } catch (cancelError) {\n setError(cancelError instanceof Error ? cancelError.message : String(cancelError))\n triggerHaptic('error')\n } finally {\n setBusyAction(null)\n }\n }, [client, load, t])\n\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center py-12\">\n <InlineSpinner />\n </div>\n )\n }\n\n const currentPlan = subscription?.plan ?? plans.find((plan) => plan.id === subscription?.planId) ?? null\n const currentTier = subscription?.tier ?? currentPlan?.tier ?? null\n\n return (\n <div className=\"space-y-6\">\n {error && (\n <div\n role=\"alert\"\n className=\"rounded-xl border border-red-300/40 bg-red-50/80 px-4 py-3 text-sm text-red-700 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-300\"\n >\n {error}\n </div>\n )}\n\n <SectionCard\n header={{\n title: t('billing.currentPlan.title'),\n subtitle: t('billing.currentPlan.description'),\n }}\n >\n {subscription && currentPlan ? (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <span className=\"text-lg font-semibold text-gray-900 dark:text-white\">\n {currentPlan.name}\n </span>\n <StatusBadge\n status={\n subscription.status === 'active' ? 'success'\n : subscription.status === 'past_due' ? 'pending'\n : 'error'\n }\n label={t(`billing.status.${subscription.status}`)}\n />\n {subscription.cancelAtPeriodEnd && (\n <Badge color=\"amber\">{t('billing.currentPlan.cancelAtPeriodEnd')}</Badge>\n )}\n </div>\n\n <dl className=\"grid grid-cols-1 gap-3 text-sm sm:grid-cols-2\">\n <div>\n <dt className=\"text-gray-500 dark:text-gray-400\">\n {t('billing.currentPlan.price')}\n </dt>\n <dd className=\"font-medium text-gray-900 dark:text-white\">\n {format.number(currentPlan.price, { style: 'currency', currency: currentPlan.currency })}\n {' '}/{' '}\n {t(`billing.interval.${currentPlan.interval}`)}\n </dd>\n </div>\n {subscription.currentPeriodEnd && (\n <div>\n <dt className=\"text-gray-500 dark:text-gray-400\">\n {t('billing.currentPlan.renewsOn')}\n </dt>\n <dd className=\"font-medium text-gray-900 dark:text-white\">\n {format.dateTime(new Date(subscription.currentPeriodEnd), { dateStyle: 'medium' })}\n </dd>\n </div>\n )}\n </dl>\n\n <div className=\"flex flex-wrap gap-2 pt-2\">\n <Button\n size=\"sm\"\n color=\"ios-glass-blue\"\n onClick={handleOpenPortal}\n loading={busyAction === 'portal'}\n disabled={busyAction !== null}\n >\n {t('billing.currentPlan.manageInPortal')}\n </Button>\n {subscription.status === 'active' && !subscription.cancelAtPeriodEnd && (\n <Button\n size=\"sm\"\n color=\"ios-glass-red\"\n onClick={handleCancel}\n loading={busyAction === 'cancel'}\n disabled={busyAction !== null}\n >\n {t('billing.currentPlan.cancel')}\n </Button>\n )}\n </div>\n </div>\n ) : (\n <p className=\"text-sm text-gray-500 dark:text-gray-400\">\n {t('billing.currentPlan.noSubscription')}\n </p>\n )}\n </SectionCard>\n\n <SectionCard\n header={{\n title: t('billing.plans.title'),\n subtitle: t('billing.plans.description'),\n }}\n >\n {plans.length === 0 ? (\n <p className=\"text-sm text-gray-500 dark:text-gray-400\">\n {t('billing.plans.empty')}\n </p>\n ) : (\n <ListCard>\n {plans.map((plan) => {\n const isCurrent = currentPlan?.id === plan.id\n const isDowngrade = currentTier != null\n && (TIER_ORDER[plan.tier] ?? 0) < (TIER_ORDER[currentTier] ?? 0)\n return (\n <ListCardItem\n key={plan.id}\n leading={\n <div className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-violet-500/20 to-blue-500/20 text-violet-600 dark:text-violet-300\">\n <span className=\"text-sm font-bold uppercase\">{plan.tier.charAt(0)}</span>\n </div>\n }\n trailing={\n isCurrent ? (\n <Badge color=\"green\">{t('billing.plans.currentBadge')}</Badge>\n ) : (\n isDowngrade ? (\n <Button\n size=\"sm\"\n plain\n onClick={() => handleStartCheckout(plan.code)}\n loading={busyAction === `checkout:${plan.code}`}\n disabled={busyAction !== null}\n >\n {t('billing.plans.downgradeAction')}\n </Button>\n ) : (\n <Button\n size=\"sm\"\n color=\"ios-glass-blue\"\n onClick={() => handleStartCheckout(plan.code)}\n loading={busyAction === `checkout:${plan.code}`}\n disabled={busyAction !== null}\n >\n {t('billing.plans.selectAction')}\n </Button>\n )\n )\n }\n >\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex items-center gap-2\">\n <p className=\"text-sm font-medium text-gray-900 dark:text-white\">\n {plan.name}\n </p>\n <span className=\"text-xs text-gray-500 dark:text-gray-400\">\n {format.number(plan.price, { style: 'currency', currency: plan.currency })}\n {' '}/{' '}\n {t(`billing.interval.${plan.interval}`)}\n </span>\n </div>\n {plan.description && (\n <p className=\"text-xs text-gray-500 dark:text-gray-400\">\n {plan.description}\n </p>\n )}\n </div>\n </ListCardItem>\n )\n })}\n </ListCard>\n )}\n </SectionCard>\n </div>\n )\n}\n"]}
|