@growflowstudio/billing-admin-ui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2152 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ BillingAdminProvider: () => BillingAdminProvider,
34
+ CursorPagination: () => CursorPagination,
35
+ DeleteConfirmDialog: () => DeleteConfirmDialog,
36
+ FeaturesPage: () => FeaturesPage,
37
+ FilterBar: () => FilterBar,
38
+ InvoiceDetailsDialog: () => InvoiceDetailsDialog,
39
+ InvoicesPage: () => InvoicesPage,
40
+ LimitInput: () => LimitInput,
41
+ PLAN_COLORS: () => PLAN_COLORS,
42
+ PaymentsPage: () => PaymentsPage,
43
+ PlanFormDialog: () => PlanFormDialog,
44
+ PlansPage: () => PlansPage,
45
+ PlansTable: () => PlansTable,
46
+ ProductFormDialog: () => ProductFormDialog,
47
+ ProductsPage: () => ProductsPage,
48
+ SkeletonRows: () => SkeletonRows,
49
+ StatusBadge: () => StatusBadge,
50
+ SubscriptionsPage: () => SubscriptionsPage,
51
+ createDefaultPlanFormData: () => createDefaultPlanFormData,
52
+ defaultProductFormData: () => defaultProductFormData,
53
+ getBillingNavItems: () => getBillingNavItems,
54
+ getPlanColorClass: () => getPlanColorClass,
55
+ useBillingAdminConfig: () => useBillingAdminConfig,
56
+ useEnabledModules: () => useEnabledModules
57
+ });
58
+ module.exports = __toCommonJS(index_exports);
59
+
60
+ // src/provider.tsx
61
+ var import_react = __toESM(require("react"));
62
+ var import_billing_admin_core = require("@growflowstudio/billing-admin-core");
63
+ var import_jsx_runtime = require("react/jsx-runtime");
64
+ var DEFAULT_MODULES = {
65
+ plans: true,
66
+ subscriptions: true,
67
+ products: true,
68
+ payments: true,
69
+ invoices: true,
70
+ features: true
71
+ };
72
+ var BillingAdminConfigContext = import_react.default.createContext(null);
73
+ function useBillingAdminConfig() {
74
+ const config = import_react.default.useContext(BillingAdminConfigContext);
75
+ if (!config) {
76
+ throw new Error(
77
+ "useBillingAdminConfig must be used within a BillingAdminProvider."
78
+ );
79
+ }
80
+ return config;
81
+ }
82
+ function useEnabledModules() {
83
+ const config = useBillingAdminConfig();
84
+ return { ...DEFAULT_MODULES, ...config.modules };
85
+ }
86
+ function BillingAdminProvider({ config, children }) {
87
+ const client = (0, import_react.useMemo)(
88
+ () => (0, import_billing_admin_core.createBillingAdminClient)({
89
+ basePath: config.basePath,
90
+ fetcher: config.fetcher
91
+ }),
92
+ [config.basePath, config.fetcher]
93
+ );
94
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BillingAdminConfigContext.Provider, { value: config, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_billing_admin_core.BillingAdminClientContext.Provider, { value: client, children }) });
95
+ }
96
+
97
+ // src/pages/PlansPage.tsx
98
+ var import_react3 = require("react");
99
+ var import_sonner = require("sonner");
100
+ var import_lucide_react4 = require("lucide-react");
101
+ var import_billing_admin_core3 = require("@growflowstudio/billing-admin-core");
102
+
103
+ // src/components/plans/PlansTable.tsx
104
+ var import_lucide_react = require("lucide-react");
105
+ var import_billing_admin_core2 = require("@growflowstudio/billing-admin-core");
106
+
107
+ // src/components/plans/plan-colors.ts
108
+ var PLAN_COLORS = [
109
+ { value: "gray", label: "Grigio", className: "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-400" },
110
+ { value: "green", label: "Verde", className: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" },
111
+ { value: "blue", label: "Blu", className: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400" },
112
+ { value: "purple", label: "Viola", className: "bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400" },
113
+ { value: "amber", label: "Ambra", className: "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400" },
114
+ { value: "red", label: "Rosso", className: "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" },
115
+ { value: "pink", label: "Rosa", className: "bg-pink-100 text-pink-700 dark:bg-pink-900/30 dark:text-pink-400" },
116
+ { value: "indigo", label: "Indaco", className: "bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400" }
117
+ ];
118
+ function getPlanColorClass(color) {
119
+ const found = PLAN_COLORS.find((c) => c.value === color);
120
+ return found?.className || PLAN_COLORS[0].className;
121
+ }
122
+
123
+ // src/primitives/utils.ts
124
+ var import_clsx = require("clsx");
125
+ var import_tailwind_merge = require("tailwind-merge");
126
+ function cn(...inputs) {
127
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
128
+ }
129
+
130
+ // src/components/shared/StatusBadge.tsx
131
+ var import_jsx_runtime2 = require("react/jsx-runtime");
132
+ function StatusBadge({ label, colorClass, className }) {
133
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
134
+ "span",
135
+ {
136
+ className: cn(
137
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors",
138
+ colorClass,
139
+ className
140
+ ),
141
+ children: label
142
+ }
143
+ );
144
+ }
145
+
146
+ // src/components/shared/SkeletonRows.tsx
147
+ var import_jsx_runtime3 = require("react/jsx-runtime");
148
+ function SkeletonRows({ rows = 5, columns = 1 }) {
149
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { colSpan: columns, className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "h-10 w-full animate-pulse rounded-md bg-muted" }) }) }, i)) });
150
+ }
151
+
152
+ // src/components/plans/PlansTable.tsx
153
+ var import_jsx_runtime4 = require("react/jsx-runtime");
154
+ var pricingModelLabels = {
155
+ flat: "Fisso",
156
+ tiered: "A Scaglioni",
157
+ per_unit: "Per Unit\xE0",
158
+ usage_based: "A Consumo"
159
+ };
160
+ function PlansTable({
161
+ plans,
162
+ isLoading,
163
+ onEdit,
164
+ onDelete,
165
+ onSync,
166
+ isSyncing
167
+ }) {
168
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("table", { className: "w-full caption-bottom text-sm", children: [
169
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("thead", { className: "[&_tr]:border-b", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("tr", { className: "border-b transition-colors hover:bg-muted/50", children: [
170
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Slug" }),
171
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Nome" }),
172
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Colore" }),
173
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Modello" }),
174
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Prezzo Mensile" }),
175
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Prezzo Annuale" }),
176
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Trial" }),
177
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Attivo" }),
178
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Stripe" }),
179
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "h-12 px-4 text-right align-middle font-medium text-muted-foreground", children: "Azioni" })
180
+ ] }) }),
181
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { className: "[&_tr:last-child]:border-0", children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SkeletonRows, { rows: 3, columns: 10 }) : plans.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { colSpan: 10, className: "p-4 text-center py-8 text-muted-foreground", children: "Nessun piano configurato" }) }) : plans.map((plan) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("tr", { className: "border-b transition-colors hover:bg-muted/50", children: [
182
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { className: "p-4 align-middle", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { className: "text-sm bg-muted px-2 py-0.5 rounded", children: plan.slug }) }),
183
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { className: "p-4 align-middle font-medium", children: plan.name }),
184
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { className: "p-4 align-middle", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StatusBadge, { label: plan.color, colorClass: getPlanColorClass(plan.color) }) }),
185
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { className: "p-4 align-middle", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StatusBadge, { label: pricingModelLabels[plan.pricing_model], colorClass: "border" }) }),
186
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { className: "p-4 align-middle", children: plan.contact_sales ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-muted-foreground", children: "Contattaci" }) : (0, import_billing_admin_core2.formatCurrency)(plan.price_monthly ?? 0, plan.currency) }),
187
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { className: "p-4 align-middle", children: plan.contact_sales ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-muted-foreground", children: "Contattaci" }) : (0, import_billing_admin_core2.formatCurrency)(plan.price_yearly ?? 0, plan.currency) }),
188
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("td", { className: "p-4 align-middle", children: [
189
+ plan.trial_days,
190
+ " giorni"
191
+ ] }),
192
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { className: "p-4 align-middle", children: plan.is_active ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Check, { className: "w-4 h-4 text-green-600" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.X, { className: "w-4 h-4 text-muted-foreground" }) }),
193
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { className: "p-4 align-middle", children: plan.stripe_price_id_monthly ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StatusBadge, { label: "Synced", colorClass: "bg-green-50 text-green-700 border-green-200" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
194
+ "button",
195
+ {
196
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 w-9 hover:bg-accent hover:text-accent-foreground",
197
+ onClick: () => onSync(plan.id),
198
+ disabled: isSyncing,
199
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Upload, { className: "w-4 h-4" })
200
+ }
201
+ ) }),
202
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { className: "p-4 align-middle text-right", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex justify-end gap-1", children: [
203
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
204
+ "button",
205
+ {
206
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 w-9 hover:bg-accent hover:text-accent-foreground",
207
+ onClick: () => onEdit(plan),
208
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Pencil, { className: "w-4 h-4" })
209
+ }
210
+ ),
211
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
212
+ "button",
213
+ {
214
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 w-9 text-destructive hover:bg-accent hover:text-destructive",
215
+ onClick: () => onDelete(plan),
216
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Trash2, { className: "w-4 h-4" })
217
+ }
218
+ )
219
+ ] }) })
220
+ ] }, plan.id)) })
221
+ ] }) });
222
+ }
223
+
224
+ // src/components/plans/PlanFormDialog.tsx
225
+ var import_react2 = require("react");
226
+ var import_react_query = require("@tanstack/react-query");
227
+ var import_lucide_react2 = require("lucide-react");
228
+
229
+ // src/components/plans/LimitInput.tsx
230
+ var import_jsx_runtime5 = require("react/jsx-runtime");
231
+ function LimitInput({
232
+ id,
233
+ label,
234
+ value,
235
+ isUnlimited,
236
+ onValueChange,
237
+ onUnlimitedChange,
238
+ min = 1
239
+ }) {
240
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "space-y-2 p-3 border rounded-lg", children: [
241
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
242
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { htmlFor: id, className: "text-sm font-medium leading-none", children: label }),
243
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", children: [
244
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
245
+ "button",
246
+ {
247
+ type: "button",
248
+ role: "switch",
249
+ "aria-checked": isUnlimited,
250
+ onClick: () => onUnlimitedChange(!isUnlimited),
251
+ className: `peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors ${isUnlimited ? "bg-primary" : "bg-input"}`,
252
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
253
+ "span",
254
+ {
255
+ className: `pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform ${isUnlimited ? "translate-x-4" : "translate-x-0"}`
256
+ }
257
+ )
258
+ }
259
+ ),
260
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-xs text-muted-foreground", children: "Illimitati" })
261
+ ] })
262
+ ] }),
263
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
264
+ "input",
265
+ {
266
+ id,
267
+ type: "number",
268
+ min,
269
+ value,
270
+ onChange: (e) => onValueChange(parseInt(e.target.value) || min),
271
+ disabled: isUnlimited,
272
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
273
+ }
274
+ )
275
+ ] });
276
+ }
277
+
278
+ // src/components/plans/PlanFormDialog.tsx
279
+ var import_jsx_runtime6 = require("react/jsx-runtime");
280
+ function createDefaultPlanFormData(limitFields = []) {
281
+ const limits = {};
282
+ const limitsUnlimited = {};
283
+ for (const field of limitFields) {
284
+ limits[field.key] = field.defaultValue ?? 1;
285
+ limitsUnlimited[field.key] = false;
286
+ }
287
+ return {
288
+ slug: "",
289
+ name: "",
290
+ description: "",
291
+ pricing_model: "flat",
292
+ price_monthly: "",
293
+ price_yearly: "",
294
+ currency: "eur",
295
+ trial_days: 14,
296
+ is_active: true,
297
+ contact_sales: false,
298
+ is_public: true,
299
+ color: "gray",
300
+ limits,
301
+ limits_unlimited: limitsUnlimited,
302
+ features: "",
303
+ sort_order: 0
304
+ };
305
+ }
306
+ function PlanFormDialog({
307
+ open,
308
+ onOpenChange,
309
+ formData,
310
+ onFormDataChange,
311
+ formErrors,
312
+ selectedPlan,
313
+ onSubmit,
314
+ isPending
315
+ }) {
316
+ const config = useBillingAdminConfig();
317
+ const limitFields = config.plans?.limitFields || [];
318
+ const featuresConfig = config.plans?.features;
319
+ const { data: featuresData } = (0, import_react_query.useQuery)({
320
+ queryKey: ["billing-admin-available-features"],
321
+ queryFn: () => featuresConfig?.fetchFn?.() ?? Promise.resolve({ features: [] }),
322
+ enabled: !!featuresConfig?.enabled && !!featuresConfig?.fetchFn
323
+ });
324
+ const availableFeatures = featuresData?.features || [];
325
+ const [selectedFeatureSlugs, setSelectedFeatureSlugs] = (0, import_react2.useState)([]);
326
+ (0, import_react2.useEffect)(() => {
327
+ if (formData.features) {
328
+ const slugs = formData.features.split("\n").map((line) => line.trim()).filter(Boolean);
329
+ setSelectedFeatureSlugs(slugs);
330
+ }
331
+ }, [formData.features]);
332
+ const handleFeatureToggle = (slug, checked) => {
333
+ const updated = checked ? [...selectedFeatureSlugs, slug] : selectedFeatureSlugs.filter((s) => s !== slug);
334
+ setSelectedFeatureSlugs(updated);
335
+ onFormDataChange({ features: updated.join("\n") });
336
+ };
337
+ if (!open) return null;
338
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
339
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => onOpenChange(false) }),
340
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative z-50 w-full max-w-2xl max-h-[90vh] overflow-y-auto rounded-lg border bg-background p-6 shadow-lg", children: [
341
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col space-y-1.5 pb-4", children: [
342
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "text-lg font-semibold leading-none tracking-tight", children: selectedPlan ? "Modifica Piano" : "Nuovo Piano" }),
343
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-muted-foreground", children: selectedPlan ? "Modifica le impostazioni del piano selezionato" : "Crea un nuovo piano di abbonamento" })
344
+ ] }),
345
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid gap-4 py-4", children: [
346
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
347
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
348
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "slug", children: "Slug *" }),
349
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
350
+ "input",
351
+ {
352
+ id: "slug",
353
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50",
354
+ value: formData.slug,
355
+ onChange: (e) => onFormDataChange({ slug: e.target.value }),
356
+ placeholder: "es. pro, enterprise",
357
+ disabled: !!selectedPlan
358
+ }
359
+ ),
360
+ formErrors.slug && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-xs text-destructive", children: formErrors.slug })
361
+ ] }),
362
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
363
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "name", children: "Nome *" }),
364
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
365
+ "input",
366
+ {
367
+ id: "name",
368
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
369
+ value: formData.name,
370
+ onChange: (e) => onFormDataChange({ name: e.target.value }),
371
+ placeholder: "es. Piano Pro"
372
+ }
373
+ ),
374
+ formErrors.name && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-xs text-destructive", children: formErrors.name })
375
+ ] })
376
+ ] }),
377
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
378
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "description", children: "Descrizione" }),
379
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
380
+ "textarea",
381
+ {
382
+ id: "description",
383
+ className: "flex min-h-[60px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
384
+ value: formData.description,
385
+ onChange: (e) => onFormDataChange({ description: e.target.value }),
386
+ placeholder: "Descrizione del piano...",
387
+ rows: 2
388
+ }
389
+ )
390
+ ] }),
391
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid grid-cols-3 gap-4", children: [
392
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
393
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-sm font-medium leading-none", children: "Modello Pricing" }),
394
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
395
+ "select",
396
+ {
397
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
398
+ value: formData.pricing_model,
399
+ onChange: (e) => onFormDataChange({ pricing_model: e.target.value }),
400
+ children: [
401
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "flat", children: "Fisso" }),
402
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "tiered", children: "A Scaglioni" }),
403
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "per_unit", children: "Per Unit\xE0" }),
404
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "usage_based", children: "A Consumo" })
405
+ ]
406
+ }
407
+ )
408
+ ] }),
409
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
410
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "price_monthly", children: "Prezzo Mensile" }),
411
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
412
+ "input",
413
+ {
414
+ id: "price_monthly",
415
+ type: "number",
416
+ step: "0.01",
417
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
418
+ value: formData.price_monthly,
419
+ onChange: (e) => onFormDataChange({ price_monthly: e.target.value }),
420
+ placeholder: "0.00"
421
+ }
422
+ )
423
+ ] }),
424
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
425
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "price_yearly", children: "Prezzo Annuale" }),
426
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
427
+ "input",
428
+ {
429
+ id: "price_yearly",
430
+ type: "number",
431
+ step: "0.01",
432
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
433
+ value: formData.price_yearly,
434
+ onChange: (e) => onFormDataChange({ price_yearly: e.target.value }),
435
+ placeholder: "0.00"
436
+ }
437
+ )
438
+ ] })
439
+ ] }),
440
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid grid-cols-3 gap-4", children: [
441
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
442
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-sm font-medium leading-none", children: "Valuta" }),
443
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
444
+ "select",
445
+ {
446
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
447
+ value: formData.currency,
448
+ onChange: (e) => onFormDataChange({ currency: e.target.value }),
449
+ children: [
450
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "eur", children: "EUR" }),
451
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "usd", children: "USD" }),
452
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "gbp", children: "GBP" })
453
+ ]
454
+ }
455
+ )
456
+ ] }),
457
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
458
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "trial_days", children: "Giorni Trial" }),
459
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { id: "trial_days", type: "number", className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", value: formData.trial_days, onChange: (e) => onFormDataChange({ trial_days: parseInt(e.target.value) || 0 }) })
460
+ ] }),
461
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
462
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "sort_order", children: "Ordine" }),
463
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { id: "sort_order", type: "number", className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", value: formData.sort_order, onChange: (e) => onFormDataChange({ sort_order: parseInt(e.target.value) || 0 }) })
464
+ ] })
465
+ ] }),
466
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
467
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
468
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-sm font-medium leading-none", children: "Colore Badge" }),
469
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
470
+ "select",
471
+ {
472
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
473
+ value: formData.color,
474
+ onChange: (e) => onFormDataChange({ color: e.target.value }),
475
+ children: PLAN_COLORS.map((color) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: color.value, children: color.label }, color.value))
476
+ }
477
+ ),
478
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
479
+ "Anteprima: ",
480
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: `inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold ${getPlanColorClass(formData.color)}`, children: formData.name || "Piano" })
481
+ ] })
482
+ ] }),
483
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-4 pt-6", children: [
484
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ToggleSwitch, { label: "Piano Attivo", checked: formData.is_active, onChange: (v) => onFormDataChange({ is_active: v }) }),
485
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ToggleSwitch, { label: "Contattaci per Prezzi", checked: formData.contact_sales, onChange: (v) => onFormDataChange({ contact_sales: v }) }),
486
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ToggleSwitch, { label: "Visibile in Homepage", checked: formData.is_public, onChange: (v) => onFormDataChange({ is_public: v }) })
487
+ ] })
488
+ ] }),
489
+ limitFields.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-3", children: [
490
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-sm font-medium leading-none", children: "Limiti Piano" }),
491
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "grid grid-cols-2 gap-3", children: limitFields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
492
+ LimitInput,
493
+ {
494
+ id: `limit_${field.key}`,
495
+ label: field.label,
496
+ value: formData.limits[field.key] ?? field.defaultValue ?? 1,
497
+ isUnlimited: formData.limits_unlimited[field.key] ?? false,
498
+ onValueChange: (v) => onFormDataChange({ limits: { ...formData.limits, [field.key]: v } }),
499
+ onUnlimitedChange: (v) => onFormDataChange({ limits_unlimited: { ...formData.limits_unlimited, [field.key]: v } })
500
+ },
501
+ field.key
502
+ )) })
503
+ ] }),
504
+ featuresConfig?.enabled && availableFeatures.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
505
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-sm font-medium leading-none", children: "Features Incluse nel Piano" }),
506
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "border rounded-lg p-3 space-y-2 max-h-64 overflow-y-auto", children: availableFeatures.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-3 p-2 hover:bg-muted/50 rounded", children: [
507
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
508
+ "input",
509
+ {
510
+ type: "checkbox",
511
+ id: `feature-${feature.slug}`,
512
+ checked: selectedFeatureSlugs.includes(feature.slug),
513
+ onChange: (e) => handleFeatureToggle(feature.slug, e.target.checked),
514
+ className: "h-4 w-4 rounded border-input"
515
+ }
516
+ ),
517
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex-1", children: [
518
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: `feature-${feature.slug}`, className: "text-sm font-medium cursor-pointer", children: feature.name }),
519
+ feature.description && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-xs text-muted-foreground", children: feature.description })
520
+ ] }),
521
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { className: "text-xs text-muted-foreground bg-muted px-2 py-0.5 rounded", children: feature.slug })
522
+ ] }, feature.slug)) }),
523
+ selectedFeatureSlugs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex flex-wrap gap-2 pt-2", children: selectedFeatureSlugs.map((slug) => {
524
+ const feature = availableFeatures.find((f) => f.slug === slug);
525
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold bg-secondary text-secondary-foreground gap-1", children: [
526
+ feature?.name || slug,
527
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: () => handleFeatureToggle(slug, false), className: "ml-1 hover:bg-destructive/20 rounded-full", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.X, { className: "h-3 w-3" }) })
528
+ ] }, slug);
529
+ }) })
530
+ ] })
531
+ ] }),
532
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-4", children: [
533
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
534
+ "button",
535
+ {
536
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground mt-2 sm:mt-0",
537
+ onClick: () => onOpenChange(false),
538
+ children: "Annulla"
539
+ }
540
+ ),
541
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
542
+ "button",
543
+ {
544
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
545
+ onClick: onSubmit,
546
+ disabled: isPending,
547
+ children: [
548
+ isPending && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.Loader2, { className: "w-4 h-4 animate-spin mr-2" }),
549
+ selectedPlan ? "Salva Modifiche" : "Crea Piano"
550
+ ]
551
+ }
552
+ )
553
+ ] })
554
+ ] })
555
+ ] });
556
+ }
557
+ function ToggleSwitch({ label, checked, onChange }) {
558
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", children: [
559
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
560
+ "button",
561
+ {
562
+ type: "button",
563
+ role: "switch",
564
+ "aria-checked": checked,
565
+ onClick: () => onChange(!checked),
566
+ className: `peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors ${checked ? "bg-primary" : "bg-input"}`,
567
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: `pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform ${checked ? "translate-x-4" : "translate-x-0"}` })
568
+ }
569
+ ),
570
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-sm font-medium leading-none", children: label })
571
+ ] });
572
+ }
573
+
574
+ // src/components/shared/DeleteConfirmDialog.tsx
575
+ var import_lucide_react3 = require("lucide-react");
576
+ var import_jsx_runtime7 = require("react/jsx-runtime");
577
+ function DeleteConfirmDialog({
578
+ isOpen,
579
+ onOpenChange,
580
+ title = "Sei sicuro?",
581
+ description,
582
+ onConfirm,
583
+ isDeleting,
584
+ confirmLabel = "Elimina",
585
+ cancelLabel = "Annulla"
586
+ }) {
587
+ if (!isOpen) return null;
588
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
589
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
590
+ "div",
591
+ {
592
+ className: "fixed inset-0 bg-black/80",
593
+ onClick: () => onOpenChange(false)
594
+ }
595
+ ),
596
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "relative z-50 w-full max-w-lg rounded-lg border bg-background p-6 shadow-lg", children: [
597
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col space-y-2 text-center sm:text-left", children: [
598
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { className: "text-lg font-semibold", children: title }),
599
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-sm text-muted-foreground", children: description })
600
+ ] }),
601
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 mt-4", children: [
602
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
603
+ "button",
604
+ {
605
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground mt-2 sm:mt-0",
606
+ onClick: () => onOpenChange(false),
607
+ children: cancelLabel
608
+ }
609
+ ),
610
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
611
+ "button",
612
+ {
613
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-destructive text-destructive-foreground hover:bg-destructive/90",
614
+ onClick: onConfirm,
615
+ disabled: isDeleting,
616
+ children: [
617
+ isDeleting && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react3.Loader2, { className: "w-4 h-4 animate-spin mr-2" }),
618
+ confirmLabel
619
+ ]
620
+ }
621
+ )
622
+ ] })
623
+ ] })
624
+ ] });
625
+ }
626
+
627
+ // src/pages/PlansPage.tsx
628
+ var import_jsx_runtime8 = require("react/jsx-runtime");
629
+ function PlansPage({ wrapper: Wrapper, header }) {
630
+ const config = useBillingAdminConfig();
631
+ const limitFields = config.plans?.limitFields || [];
632
+ const publicPlansConfig = config.plans?.publicPlans;
633
+ const [showForm, setShowForm] = (0, import_react3.useState)(false);
634
+ const [showDeleteDialog, setShowDeleteDialog] = (0, import_react3.useState)(false);
635
+ const [selectedPlan, setSelectedPlan] = (0, import_react3.useState)(null);
636
+ const [formData, setFormData] = (0, import_react3.useState)(createDefaultPlanFormData(limitFields));
637
+ const [formErrors, setFormErrors] = (0, import_react3.useState)({});
638
+ const { data, isLoading, refetch } = (0, import_billing_admin_core3.useAdminPlans)();
639
+ const createMutation = (0, import_billing_admin_core3.useCreatePlan)();
640
+ const updateMutation = (0, import_billing_admin_core3.useUpdatePlan)();
641
+ const deleteMutation = (0, import_billing_admin_core3.useDeletePlan)();
642
+ const syncMutation = (0, import_billing_admin_core3.useSyncPlanToStripe)();
643
+ const [publicPlanSlugs, setPublicPlanSlugs] = (0, import_react3.useState)([]);
644
+ (0, import_react3.useState)(() => {
645
+ if (publicPlansConfig?.enabled && publicPlansConfig.fetchFn) {
646
+ publicPlansConfig.fetchFn().then((r) => setPublicPlanSlugs(r.public_plan_slugs));
647
+ }
648
+ });
649
+ const plans = data?.items || [];
650
+ const resetForm = () => {
651
+ setFormData(createDefaultPlanFormData(limitFields));
652
+ setSelectedPlan(null);
653
+ setFormErrors({});
654
+ };
655
+ const openCreateForm = () => {
656
+ resetForm();
657
+ setShowForm(true);
658
+ };
659
+ const openEditForm = (plan) => {
660
+ setSelectedPlan(plan);
661
+ const planLimits = plan.limits || {};
662
+ const limits = {};
663
+ const limitsUnlimited = {};
664
+ for (const field of limitFields) {
665
+ const val = planLimits[field.key];
666
+ limits[field.key] = val === -1 ? field.defaultValue ?? 1 : val || field.defaultValue || 1;
667
+ limitsUnlimited[field.key] = val === -1;
668
+ }
669
+ setFormData({
670
+ slug: plan.slug,
671
+ name: plan.name,
672
+ description: plan.description || "",
673
+ pricing_model: plan.pricing_model,
674
+ price_monthly: plan.price_monthly ? plan.price_monthly.toString() : "",
675
+ price_yearly: plan.price_yearly ? plan.price_yearly.toString() : "",
676
+ currency: plan.currency,
677
+ trial_days: plan.trial_days,
678
+ is_active: plan.is_active,
679
+ contact_sales: plan.contact_sales,
680
+ is_public: publicPlanSlugs.includes(plan.slug),
681
+ color: plan.color || "gray",
682
+ limits,
683
+ limits_unlimited: limitsUnlimited,
684
+ features: plan.features?.join("\n") || "",
685
+ sort_order: plan.sort_order
686
+ });
687
+ setShowForm(true);
688
+ };
689
+ const handleValidate = () => {
690
+ const errors = {};
691
+ const slugErr = (0, import_billing_admin_core3.validateSlug)(formData.slug);
692
+ if (slugErr) errors.slug = slugErr;
693
+ const nameErr = (0, import_billing_admin_core3.validateRequired)(formData.name, "Il nome");
694
+ if (nameErr) errors.name = nameErr;
695
+ setFormErrors(errors);
696
+ return Object.keys(errors).length === 0;
697
+ };
698
+ const handleSubmit = async () => {
699
+ if (!handleValidate()) return;
700
+ const limits = {};
701
+ for (const field of limitFields) {
702
+ limits[field.key] = formData.limits_unlimited[field.key] ? -1 : formData.limits[field.key] ?? 1;
703
+ }
704
+ const planData = {
705
+ slug: formData.slug,
706
+ name: formData.name,
707
+ description: formData.description || void 0,
708
+ pricing_model: formData.pricing_model,
709
+ price_monthly: formData.price_monthly ? parseFloat(formData.price_monthly) : void 0,
710
+ price_yearly: formData.price_yearly ? parseFloat(formData.price_yearly) : void 0,
711
+ currency: formData.currency,
712
+ trial_days: formData.trial_days,
713
+ is_active: formData.is_active,
714
+ contact_sales: formData.contact_sales,
715
+ color: formData.color,
716
+ limits: Object.keys(limits).length > 0 ? limits : void 0,
717
+ features: formData.features.split("\n").filter((f) => f.trim()),
718
+ sort_order: formData.sort_order
719
+ };
720
+ if (publicPlansConfig?.enabled && publicPlansConfig.updateFn) {
721
+ const isCurrentlyPublic = publicPlanSlugs.includes(formData.slug);
722
+ if (formData.is_public !== isCurrentlyPublic) {
723
+ try {
724
+ const newSlugs = formData.is_public ? [...publicPlanSlugs, formData.slug] : publicPlanSlugs.filter((s) => s !== formData.slug);
725
+ await publicPlansConfig.updateFn(newSlugs);
726
+ setPublicPlanSlugs(newSlugs);
727
+ } catch {
728
+ import_sonner.toast.error("Errore nell'aggiornamento della visibilit\xE0 pubblica");
729
+ }
730
+ }
731
+ }
732
+ if (selectedPlan) {
733
+ updateMutation.mutate({ id: selectedPlan.id, data: planData }, {
734
+ onSuccess: () => {
735
+ import_sonner.toast.success("Piano aggiornato con successo");
736
+ setShowForm(false);
737
+ resetForm();
738
+ },
739
+ onError: (err) => import_sonner.toast.error(`Errore: ${err.message}`)
740
+ });
741
+ } else {
742
+ createMutation.mutate(planData, {
743
+ onSuccess: () => {
744
+ import_sonner.toast.success("Piano creato con successo");
745
+ setShowForm(false);
746
+ resetForm();
747
+ },
748
+ onError: (err) => import_sonner.toast.error(`Errore: ${err.message}`)
749
+ });
750
+ }
751
+ };
752
+ const handleDelete = () => {
753
+ if (selectedPlan) {
754
+ deleteMutation.mutate(selectedPlan.id, {
755
+ onSuccess: () => {
756
+ import_sonner.toast.success("Piano eliminato con successo");
757
+ setShowDeleteDialog(false);
758
+ setSelectedPlan(null);
759
+ },
760
+ onError: (err) => import_sonner.toast.error(`Errore: ${err.message}`)
761
+ });
762
+ }
763
+ };
764
+ const content = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
765
+ header,
766
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-4", children: [
767
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
768
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
769
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.LayoutGrid, { className: "w-4 h-4" }),
770
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
771
+ plans.length,
772
+ " piani configurati"
773
+ ] })
774
+ ] }),
775
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex gap-2", children: [
776
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-3 border border-input bg-background hover:bg-accent", onClick: () => refetch(), children: [
777
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.RefreshCw, { className: "w-4 h-4 mr-2" }),
778
+ "Aggiorna"
779
+ ] }),
780
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90", onClick: openCreateForm, children: [
781
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.Plus, { className: "w-4 h-4 mr-2" }),
782
+ "Nuovo Piano"
783
+ ] })
784
+ ] })
785
+ ] }),
786
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rounded-lg border bg-card text-card-foreground shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "p-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
787
+ PlansTable,
788
+ {
789
+ plans,
790
+ isLoading,
791
+ onEdit: openEditForm,
792
+ onDelete: (plan) => {
793
+ setSelectedPlan(plan);
794
+ setShowDeleteDialog(true);
795
+ },
796
+ onSync: (id) => syncMutation.mutate(id, {
797
+ onSuccess: () => import_sonner.toast.success("Piano sincronizzato con Stripe"),
798
+ onError: (err) => import_sonner.toast.error(`Errore sync: ${err.message}`)
799
+ }),
800
+ isSyncing: syncMutation.isPending
801
+ }
802
+ ) }) })
803
+ ] }),
804
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
805
+ PlanFormDialog,
806
+ {
807
+ open: showForm,
808
+ onOpenChange: setShowForm,
809
+ formData,
810
+ onFormDataChange: (data2) => setFormData((prev) => ({ ...prev, ...data2 })),
811
+ formErrors,
812
+ selectedPlan,
813
+ onSubmit: handleSubmit,
814
+ isPending: createMutation.isPending || updateMutation.isPending
815
+ }
816
+ ),
817
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
818
+ DeleteConfirmDialog,
819
+ {
820
+ isOpen: showDeleteDialog,
821
+ onOpenChange: setShowDeleteDialog,
822
+ title: "Eliminare Piano?",
823
+ description: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
824
+ "Stai per eliminare il piano ",
825
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("strong", { children: selectedPlan?.name }),
826
+ " (",
827
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { children: selectedPlan?.slug }),
828
+ "). Questa azione non pu\xF2 essere annullata."
829
+ ] }),
830
+ onConfirm: handleDelete,
831
+ isDeleting: deleteMutation.isPending,
832
+ confirmLabel: "Elimina Piano"
833
+ }
834
+ )
835
+ ] });
836
+ return Wrapper ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Wrapper, { children: content }) : content;
837
+ }
838
+
839
+ // src/pages/ProductsPage.tsx
840
+ var import_sonner2 = require("sonner");
841
+ var import_lucide_react6 = require("lucide-react");
842
+ var import_react_query2 = require("@tanstack/react-query");
843
+ var import_billing_admin_core4 = require("@growflowstudio/billing-admin-core");
844
+
845
+ // src/components/products/ProductFormDialog.tsx
846
+ var import_react4 = require("react");
847
+ var import_lucide_react5 = require("lucide-react");
848
+ var import_jsx_runtime9 = require("react/jsx-runtime");
849
+ var defaultProductFormData = {
850
+ name: "",
851
+ description: "",
852
+ price_cents: "",
853
+ currency: "eur",
854
+ recurring_interval: "",
855
+ category: "feature",
856
+ is_active: true,
857
+ sync_stripe: true,
858
+ feature_slug: "",
859
+ extra_data: "{}"
860
+ };
861
+ function ProductFormDialog({
862
+ isOpen,
863
+ onOpenChange,
864
+ mode,
865
+ product,
866
+ features = [],
867
+ onSubmit,
868
+ isSubmitting
869
+ }) {
870
+ const [formData, setFormData] = (0, import_react4.useState)(defaultProductFormData);
871
+ const [formErrors, setFormErrors] = (0, import_react4.useState)({});
872
+ (0, import_react4.useEffect)(() => {
873
+ if (isOpen) {
874
+ if (mode === "edit" && product) {
875
+ const featureSlug = product.extra_data?.feature_slug || "";
876
+ setFormData({
877
+ name: product.name,
878
+ description: product.description || "",
879
+ price_cents: (product.price_cents / 100).toString(),
880
+ currency: product.currency,
881
+ recurring_interval: product.recurring_interval || "",
882
+ category: product.category || "feature",
883
+ is_active: product.is_active,
884
+ sync_stripe: false,
885
+ feature_slug: featureSlug,
886
+ extra_data: product.extra_data ? JSON.stringify(product.extra_data, null, 2) : "{}"
887
+ });
888
+ } else {
889
+ setFormData(defaultProductFormData);
890
+ }
891
+ setFormErrors({});
892
+ }
893
+ }, [isOpen, mode, product]);
894
+ (0, import_react4.useEffect)(() => {
895
+ if (formData.feature_slug) {
896
+ setFormData((prev) => {
897
+ try {
898
+ const parsedData = JSON.parse(prev.extra_data);
899
+ const updated = { ...parsedData, feature_slug: formData.feature_slug };
900
+ return { ...prev, extra_data: JSON.stringify(updated, null, 2) };
901
+ } catch {
902
+ return { ...prev, extra_data: JSON.stringify({ feature_slug: formData.feature_slug }, null, 2) };
903
+ }
904
+ });
905
+ }
906
+ }, [formData.feature_slug]);
907
+ const validateForm = () => {
908
+ const errors = {};
909
+ if (!formData.name.trim()) errors.name = "Il nome \xE8 obbligatorio";
910
+ if (!formData.price_cents || parseFloat(formData.price_cents) < 0) errors.price_cents = "Il prezzo deve essere un numero positivo";
911
+ try {
912
+ JSON.parse(formData.extra_data);
913
+ } catch {
914
+ errors.extra_data = "JSON non valido per extra_data";
915
+ }
916
+ setFormErrors(errors);
917
+ return Object.keys(errors).length === 0;
918
+ };
919
+ const handleSubmit = () => {
920
+ if (validateForm()) onSubmit(formData);
921
+ };
922
+ if (!isOpen) return null;
923
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
924
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => onOpenChange(false) }),
925
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "relative z-50 w-full max-w-xl max-h-[90vh] overflow-y-auto rounded-lg border bg-background p-6 shadow-lg", children: [
926
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col space-y-1.5 pb-4", children: [
927
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { className: "text-lg font-semibold", children: mode === "create" ? "Nuovo Prodotto" : "Modifica Prodotto" }),
928
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-sm text-muted-foreground", children: mode === "create" ? "Crea un nuovo prodotto / feature acquistabile" : "Modifica le impostazioni del prodotto selezionato" })
929
+ ] }),
930
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "grid gap-4 py-4", children: [
931
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
932
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-sm font-medium leading-none", children: "Nome *" }),
933
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", value: formData.name, onChange: (e) => setFormData({ ...formData, name: e.target.value }), placeholder: "es. Ottimizza Dati Prodotti" }),
934
+ formErrors.name && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-xs text-destructive", children: formErrors.name })
935
+ ] }),
936
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
937
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-sm font-medium leading-none", children: "Descrizione" }),
938
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("textarea", { className: "flex min-h-[60px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", value: formData.description, onChange: (e) => setFormData({ ...formData, description: e.target.value }), rows: 2 })
939
+ ] }),
940
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
941
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
942
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-sm font-medium leading-none", children: "Prezzo" }),
943
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { type: "number", step: "0.01", className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", value: formData.price_cents, onChange: (e) => setFormData({ ...formData, price_cents: e.target.value }), placeholder: "49.00" }),
944
+ formErrors.price_cents && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-xs text-destructive", children: formErrors.price_cents })
945
+ ] }),
946
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
947
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-sm font-medium leading-none", children: "Valuta" }),
948
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("select", { className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", value: formData.currency, onChange: (e) => setFormData({ ...formData, currency: e.target.value }), children: [
949
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "eur", children: "EUR" }),
950
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "usd", children: "USD" }),
951
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "gbp", children: "GBP" })
952
+ ] })
953
+ ] })
954
+ ] }),
955
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
956
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
957
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-sm font-medium leading-none", children: "Ricorrenza" }),
958
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("select", { className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", value: formData.recurring_interval || "one_time", onChange: (e) => setFormData({ ...formData, recurring_interval: e.target.value === "one_time" ? "" : e.target.value }), children: [
959
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "one_time", children: "Una tantum" }),
960
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "month", children: "Mensile" }),
961
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "year", children: "Annuale" })
962
+ ] })
963
+ ] }),
964
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
965
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-sm font-medium leading-none", children: "Categoria" }),
966
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("select", { className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", value: formData.category, onChange: (e) => setFormData({ ...formData, category: e.target.value }), children: [
967
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "feature", children: "Feature" }),
968
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "addon", children: "Add-on" }),
969
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "service", children: "Servizio" }),
970
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "credits", children: "Crediti" })
971
+ ] })
972
+ ] })
973
+ ] }),
974
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-4", children: [
975
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToggleRow, { label: "Prodotto Attivo", checked: formData.is_active, onChange: (v) => setFormData({ ...formData, is_active: v }) }),
976
+ mode === "create" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ToggleRow, { label: "Sincronizza su Stripe", checked: formData.sync_stripe, onChange: (v) => setFormData({ ...formData, sync_stripe: v }) })
977
+ ] }),
978
+ features.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
979
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-sm font-medium leading-none", children: "Collega a Feature" }),
980
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
981
+ "select",
982
+ {
983
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
984
+ value: formData.feature_slug || "none",
985
+ onChange: (e) => setFormData({ ...formData, feature_slug: e.target.value === "none" ? "" : e.target.value }),
986
+ children: [
987
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "none", children: "Nessuna feature" }),
988
+ features.map((f) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("option", { value: f.slug, children: [
989
+ f.name,
990
+ " (",
991
+ f.slug,
992
+ ")"
993
+ ] }, f.slug))
994
+ ]
995
+ }
996
+ ),
997
+ formData.feature_slug && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2 p-2 bg-blue-50 border border-blue-200 rounded text-sm", children: [
998
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.Link, { className: "w-4 h-4 text-blue-600" }),
999
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-blue-900", children: [
1000
+ "Collegato a: ",
1001
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: formData.feature_slug })
1002
+ ] })
1003
+ ] })
1004
+ ] }),
1005
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
1006
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-sm font-medium leading-none", children: "Extra Data (JSON)" }),
1007
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("textarea", { className: "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm font-mono focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", value: formData.extra_data, onChange: (e) => setFormData({ ...formData, extra_data: e.target.value }), rows: 3 }),
1008
+ formErrors.extra_data && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-xs text-destructive", children: formErrors.extra_data })
1009
+ ] })
1010
+ ] }),
1011
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-4", children: [
1012
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent mt-2 sm:mt-0", onClick: () => onOpenChange(false), children: "Annulla" }),
1013
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50", onClick: handleSubmit, disabled: isSubmitting, children: [
1014
+ isSubmitting && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.Loader2, { className: "w-4 h-4 animate-spin mr-2" }),
1015
+ mode === "create" ? "Crea Prodotto" : "Salva Modifiche"
1016
+ ] })
1017
+ ] })
1018
+ ] })
1019
+ ] });
1020
+ }
1021
+ function ToggleRow({ label, checked, onChange }) {
1022
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2", children: [
1023
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1024
+ "button",
1025
+ {
1026
+ type: "button",
1027
+ role: "switch",
1028
+ "aria-checked": checked,
1029
+ onClick: () => onChange(!checked),
1030
+ className: `inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors ${checked ? "bg-primary" : "bg-input"}`,
1031
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: `pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg transition-transform ${checked ? "translate-x-4" : "translate-x-0"}` })
1032
+ }
1033
+ ),
1034
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-sm font-medium leading-none", children: label })
1035
+ ] });
1036
+ }
1037
+
1038
+ // src/pages/ProductsPage.tsx
1039
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1040
+ function ProductsPage({ wrapper: Wrapper, header }) {
1041
+ const config = useBillingAdminConfig();
1042
+ const featuresConfig = config.products?.features;
1043
+ const createDialog = (0, import_billing_admin_core4.useDialogState)();
1044
+ const editDialog = (0, import_billing_admin_core4.useDialogState)();
1045
+ const deleteDialog = (0, import_billing_admin_core4.useDialogState)();
1046
+ const { data, isLoading, refetch } = (0, import_billing_admin_core4.useAdminProducts)({ is_active: true });
1047
+ const createMutation = (0, import_billing_admin_core4.useCreateProduct)();
1048
+ const updateMutation = (0, import_billing_admin_core4.useUpdateProduct)();
1049
+ const deleteMutation = (0, import_billing_admin_core4.useDeleteProduct)();
1050
+ const syncMutation = (0, import_billing_admin_core4.useSyncProductToStripe)();
1051
+ const { data: featuresData } = (0, import_react_query2.useQuery)({
1052
+ queryKey: ["billing-admin-product-features"],
1053
+ queryFn: () => featuresConfig?.fetchFn?.() ?? Promise.resolve({ features: [] }),
1054
+ enabled: !!featuresConfig?.enabled && !!featuresConfig?.fetchFn
1055
+ });
1056
+ const products = data?.items || [];
1057
+ const features = featuresData?.features || [];
1058
+ const handleCreateSubmit = (formData) => {
1059
+ const priceCents = Math.round(parseFloat(formData.price_cents) * 100);
1060
+ const createData = {
1061
+ name: formData.name,
1062
+ description: formData.description || void 0,
1063
+ price_cents: priceCents,
1064
+ currency: formData.currency,
1065
+ recurring_interval: formData.recurring_interval || void 0,
1066
+ category: formData.category || void 0,
1067
+ is_active: formData.is_active,
1068
+ sync_stripe: formData.sync_stripe,
1069
+ extra_data: JSON.parse(formData.extra_data)
1070
+ };
1071
+ createMutation.mutate(createData, {
1072
+ onSuccess: () => {
1073
+ import_sonner2.toast.success("Prodotto creato");
1074
+ createDialog.close();
1075
+ },
1076
+ onError: (err) => import_sonner2.toast.error(`Errore: ${err.message}`)
1077
+ });
1078
+ };
1079
+ const handleEditSubmit = (formData) => {
1080
+ if (!editDialog.data) return;
1081
+ const priceCents = Math.round(parseFloat(formData.price_cents) * 100);
1082
+ const updateData = {
1083
+ name: formData.name,
1084
+ description: formData.description || void 0,
1085
+ price_cents: priceCents,
1086
+ currency: formData.currency,
1087
+ recurring_interval: formData.recurring_interval || void 0,
1088
+ category: formData.category || void 0,
1089
+ is_active: formData.is_active,
1090
+ extra_data: JSON.parse(formData.extra_data)
1091
+ };
1092
+ updateMutation.mutate({ id: editDialog.data.id, data: updateData }, {
1093
+ onSuccess: () => {
1094
+ import_sonner2.toast.success("Prodotto aggiornato");
1095
+ editDialog.close();
1096
+ },
1097
+ onError: (err) => import_sonner2.toast.error(`Errore: ${err.message}`)
1098
+ });
1099
+ };
1100
+ const handleDeleteConfirm = () => {
1101
+ if (deleteDialog.data) {
1102
+ deleteMutation.mutate(deleteDialog.data.id, {
1103
+ onSuccess: () => {
1104
+ import_sonner2.toast.success("Prodotto disattivato");
1105
+ deleteDialog.close();
1106
+ },
1107
+ onError: (err) => import_sonner2.toast.error(`Errore: ${err.message}`)
1108
+ });
1109
+ }
1110
+ };
1111
+ const content = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1112
+ header,
1113
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-4", children: [
1114
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
1115
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
1116
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.Package, { className: "w-4 h-4" }),
1117
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
1118
+ products.length,
1119
+ " prodotti configurati"
1120
+ ] })
1121
+ ] }),
1122
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex gap-2", children: [
1123
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-3 border border-input bg-background hover:bg-accent", onClick: () => refetch(), children: [
1124
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.RefreshCw, { className: "w-4 h-4 mr-2" }),
1125
+ "Aggiorna"
1126
+ ] }),
1127
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90", onClick: () => createDialog.open(), children: [
1128
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.Plus, { className: "w-4 h-4 mr-2" }),
1129
+ "Nuovo Prodotto"
1130
+ ] })
1131
+ ] })
1132
+ ] }),
1133
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "rounded-lg border bg-card shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("table", { className: "w-full caption-bottom text-sm", children: [
1134
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("thead", { className: "[&_tr]:border-b", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
1135
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Nome" }),
1136
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Categoria" }),
1137
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Prezzo" }),
1138
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Ricorrenza" }),
1139
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Attivo" }),
1140
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Stripe" }),
1141
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("th", { className: "h-12 px-4 text-right font-medium text-muted-foreground", children: "Azioni" })
1142
+ ] }) }),
1143
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("tbody", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SkeletonRows, { rows: 3, columns: 7 }) : products.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { colSpan: 7, className: "p-4 text-center py-8 text-muted-foreground", children: "Nessun prodotto configurato" }) }) : products.map((product) => {
1144
+ const hasFeature = !!product.extra_data?.feature_slug;
1145
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
1146
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
1147
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
1148
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-medium", children: product.name }),
1149
+ hasFeature && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StatusBadge, { label: "Feature", colorClass: "bg-blue-50 text-blue-700 border-blue-200" })
1150
+ ] }),
1151
+ product.description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-sm text-muted-foreground truncate max-w-xs", children: product.description })
1152
+ ] }) }),
1153
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StatusBadge, { label: product.category || "feature", colorClass: "border" }) }),
1154
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { className: "p-4", children: (0, import_billing_admin_core4.formatCurrencyFromCents)(product.price_cents, product.currency) }),
1155
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { className: "p-4", children: product.recurring_interval ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StatusBadge, { label: product.recurring_interval === "month" ? "Mensile" : product.recurring_interval === "year" ? "Annuale" : product.recurring_interval, colorClass: "bg-secondary text-secondary-foreground border-transparent" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-muted-foreground", children: "Una tantum" }) }),
1156
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { className: "p-4", children: product.is_active ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.Check, { className: "w-4 h-4 text-green-600" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.X, { className: "w-4 h-4 text-muted-foreground" }) }),
1157
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { className: "p-4", children: product.stripe_product_id ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StatusBadge, { label: "Synced", colorClass: "bg-green-50 text-green-700 border-green-200" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "inline-flex items-center justify-center rounded-md h-9 w-9 hover:bg-accent", onClick: () => syncMutation.mutate(product.id, { onSuccess: () => import_sonner2.toast.success("Sincronizzato"), onError: (err) => import_sonner2.toast.error(`Errore: ${err.message}`) }), disabled: syncMutation.isPending, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.Upload, { className: "w-4 h-4" }) }) }),
1158
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("td", { className: "p-4 text-right", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-end gap-1", children: [
1159
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "inline-flex items-center justify-center rounded-md h-9 w-9 hover:bg-accent", onClick: () => editDialog.open(product), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.Pencil, { className: "w-4 h-4" }) }),
1160
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "inline-flex items-center justify-center rounded-md h-9 w-9 text-destructive hover:bg-accent", onClick: () => deleteDialog.open(product), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.Trash2, { className: "w-4 h-4" }) })
1161
+ ] }) })
1162
+ ] }, product.id);
1163
+ }) })
1164
+ ] }) }) })
1165
+ ] }),
1166
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ProductFormDialog, { isOpen: createDialog.isOpen, onOpenChange: (open) => open ? createDialog.open() : createDialog.close(), mode: "create", features, onSubmit: handleCreateSubmit, isSubmitting: createMutation.isPending }),
1167
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ProductFormDialog, { isOpen: editDialog.isOpen, onOpenChange: (open) => open ? editDialog.open(editDialog.data) : editDialog.close(), mode: "edit", product: editDialog.data, features, onSubmit: handleEditSubmit, isSubmitting: updateMutation.isPending }),
1168
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DeleteConfirmDialog, { isOpen: deleteDialog.isOpen, onOpenChange: (open) => !open && deleteDialog.close(), title: "Disattivare Prodotto?", description: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1169
+ "Stai per disattivare il prodotto ",
1170
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: deleteDialog.data?.name }),
1171
+ ". Il prodotto non sar\xE0 pi\xF9 acquistabile."
1172
+ ] }), onConfirm: handleDeleteConfirm, isDeleting: deleteMutation.isPending, confirmLabel: "Disattiva Prodotto" })
1173
+ ] });
1174
+ return Wrapper ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Wrapper, { children: content }) : content;
1175
+ }
1176
+
1177
+ // src/pages/SubscriptionsPage.tsx
1178
+ var import_react5 = require("react");
1179
+ var import_sonner3 = require("sonner");
1180
+ var import_lucide_react8 = require("lucide-react");
1181
+ var import_billing_admin_core5 = require("@growflowstudio/billing-admin-core");
1182
+
1183
+ // src/components/shared/FilterBar.tsx
1184
+ var import_lucide_react7 = require("lucide-react");
1185
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1186
+ function FilterBar({
1187
+ searchPlaceholder = "Cerca...",
1188
+ searchValue,
1189
+ onSearchChange,
1190
+ statusFilter,
1191
+ onStatusFilterChange,
1192
+ statusOptions,
1193
+ onRefresh
1194
+ }) {
1195
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rounded-lg border bg-card text-card-foreground shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "p-6 pt-6", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col sm:flex-row gap-4", children: [
1196
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "relative flex-1", children: [
1197
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react7.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" }),
1198
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1199
+ "input",
1200
+ {
1201
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 pl-9",
1202
+ placeholder: searchPlaceholder,
1203
+ value: searchValue,
1204
+ onChange: (e) => onSearchChange(e.target.value)
1205
+ }
1206
+ )
1207
+ ] }),
1208
+ statusOptions && onStatusFilterChange && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1209
+ "select",
1210
+ {
1211
+ className: "flex h-10 w-[180px] rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
1212
+ value: statusFilter || "all",
1213
+ onChange: (e) => onStatusFilterChange(e.target.value),
1214
+ children: statusOptions.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
1215
+ }
1216
+ ),
1217
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1218
+ "button",
1219
+ {
1220
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground",
1221
+ onClick: onRefresh,
1222
+ children: [
1223
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react7.RefreshCw, { className: "w-4 h-4 mr-2" }),
1224
+ "Aggiorna"
1225
+ ]
1226
+ }
1227
+ )
1228
+ ] }) }) });
1229
+ }
1230
+
1231
+ // src/pages/SubscriptionsPage.tsx
1232
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1233
+ var statusFilterOptions = [
1234
+ { value: "all", label: "Tutti gli stati" },
1235
+ { value: "active", label: "Attivi" },
1236
+ { value: "trialing", label: "In Prova" },
1237
+ { value: "past_due", label: "Scaduti" },
1238
+ { value: "canceled", label: "Cancellati" },
1239
+ { value: "unpaid", label: "Non Pagati" }
1240
+ ];
1241
+ function SubscriptionsPage({ wrapper: Wrapper, header }) {
1242
+ const [search, setSearch] = (0, import_react5.useState)("");
1243
+ const [statusFilter, setStatusFilter] = (0, import_react5.useState)("all");
1244
+ const [selectedSub, setSelectedSub] = (0, import_react5.useState)(null);
1245
+ const [showDetails, setShowDetails] = (0, import_react5.useState)(false);
1246
+ const [showCancelDialog, setShowCancelDialog] = (0, import_react5.useState)(false);
1247
+ const [cancelAtPeriodEnd, setCancelAtPeriodEnd] = (0, import_react5.useState)(true);
1248
+ const { data, isLoading, refetch } = (0, import_billing_admin_core5.useAdminSubscriptions)({
1249
+ search: search || void 0,
1250
+ status: statusFilter !== "all" ? statusFilter : void 0,
1251
+ limit: 100
1252
+ });
1253
+ const cancelMutation = (0, import_billing_admin_core5.useCancelSubscription)();
1254
+ const subscriptions = data?.items || [];
1255
+ const total = data?.total || 0;
1256
+ const handleCancel = () => {
1257
+ if (selectedSub) {
1258
+ cancelMutation.mutate({ id: selectedSub.id, atPeriodEnd: cancelAtPeriodEnd }, {
1259
+ onSuccess: () => {
1260
+ import_sonner3.toast.success("Abbonamento cancellato");
1261
+ setShowCancelDialog(false);
1262
+ setSelectedSub(null);
1263
+ },
1264
+ onError: (err) => import_sonner3.toast.error(`Errore: ${err.message}`)
1265
+ });
1266
+ }
1267
+ };
1268
+ const content = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1269
+ header,
1270
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-4", children: [
1271
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1272
+ FilterBar,
1273
+ {
1274
+ searchPlaceholder: "Cerca per email o ID esterno...",
1275
+ searchValue: search,
1276
+ onSearchChange: setSearch,
1277
+ statusFilter,
1278
+ onStatusFilterChange: setStatusFilter,
1279
+ statusOptions: statusFilterOptions,
1280
+ onRefresh: refetch
1281
+ }
1282
+ ),
1283
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
1284
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.CreditCard, { className: "w-4 h-4" }),
1285
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
1286
+ total,
1287
+ " abbonament",
1288
+ total === 1 ? "o" : "i",
1289
+ " trovati"
1290
+ ] })
1291
+ ] }),
1292
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rounded-lg border bg-card shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("table", { className: "w-full caption-bottom text-sm", children: [
1293
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("thead", { className: "[&_tr]:border-b", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
1294
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Subscriber" }),
1295
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Piano" }),
1296
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Stato" }),
1297
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Ciclo" }),
1298
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Periodo" }),
1299
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Trial End" }),
1300
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Creato" }),
1301
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { className: "h-12 px-4 text-right font-medium text-muted-foreground", children: "Azioni" })
1302
+ ] }) }),
1303
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tbody", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SkeletonRows, { rows: 5, columns: 8 }) : subscriptions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { colSpan: 8, className: "p-4 text-center py-8 text-muted-foreground", children: "Nessun abbonamento trovato" }) }) : subscriptions.map((sub) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
1304
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col", children: [
1305
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "font-medium", children: sub.subscriber_email }),
1306
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-xs text-muted-foreground", children: sub.subscriber_external_id })
1307
+ ] }) }),
1308
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("td", { className: "p-4", children: [
1309
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "font-medium", children: sub.plan_name }),
1310
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "text-xs text-muted-foreground ml-1", children: [
1311
+ "(",
1312
+ sub.plan_slug,
1313
+ ")"
1314
+ ] })
1315
+ ] }),
1316
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(StatusBadge, { label: import_billing_admin_core5.subscriptionStatusLabels[sub.status], colorClass: import_billing_admin_core5.subscriptionStatusColors[sub.status] }) }),
1317
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "p-4 capitalize", children: sub.billing_cycle }),
1318
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("td", { className: "p-4 text-sm", children: [
1319
+ (0, import_billing_admin_core5.formatDate)(sub.current_period_start),
1320
+ " - ",
1321
+ (0, import_billing_admin_core5.formatDate)(sub.current_period_end)
1322
+ ] }),
1323
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "p-4", children: (0, import_billing_admin_core5.formatDate)(sub.trial_end) }),
1324
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "p-4", children: (0, import_billing_admin_core5.formatDate)(sub.created_at) }),
1325
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "p-4 text-right", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex justify-end gap-1", children: [
1326
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { className: "inline-flex items-center justify-center rounded-md h-9 w-9 hover:bg-accent", onClick: () => {
1327
+ setSelectedSub(sub);
1328
+ setShowDetails(true);
1329
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.Eye, { className: "w-4 h-4" }) }),
1330
+ sub.status !== "canceled" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { className: "inline-flex items-center justify-center rounded-md h-9 w-9 text-destructive hover:bg-accent", onClick: () => {
1331
+ setSelectedSub(sub);
1332
+ setShowCancelDialog(true);
1333
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.XCircle, { className: "w-4 h-4" }) })
1334
+ ] }) })
1335
+ ] }, sub.id)) })
1336
+ ] }) }) })
1337
+ ] }),
1338
+ showDetails && selectedSub && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
1339
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => setShowDetails(false) }),
1340
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "relative z-50 w-full max-w-lg rounded-lg border bg-background p-6 shadow-lg", children: [
1341
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { className: "text-lg font-semibold mb-1", children: "Dettagli Abbonamento" }),
1342
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground mb-4", children: "Informazioni complete" }),
1343
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-4", children: [
1344
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
1345
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
1346
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground", children: "Subscriber" }),
1347
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 mt-1", children: [
1348
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.User, { className: "w-4 h-4 text-muted-foreground" }),
1349
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "font-medium", children: selectedSub.subscriber_email })
1350
+ ] }),
1351
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-xs text-muted-foreground mt-1", children: [
1352
+ "ID: ",
1353
+ selectedSub.subscriber_external_id
1354
+ ] })
1355
+ ] }),
1356
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
1357
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground", children: "Piano" }),
1358
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 mt-1", children: [
1359
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.CreditCard, { className: "w-4 h-4 text-muted-foreground" }),
1360
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "font-medium", children: selectedSub.plan_name })
1361
+ ] })
1362
+ ] })
1363
+ ] }),
1364
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
1365
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
1366
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground", children: "Stato" }),
1367
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(StatusBadge, { label: import_billing_admin_core5.subscriptionStatusLabels[selectedSub.status], colorClass: import_billing_admin_core5.subscriptionStatusColors[selectedSub.status], className: "mt-1" })
1368
+ ] }),
1369
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
1370
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground", children: "Ciclo" }),
1371
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "font-medium capitalize mt-1", children: selectedSub.billing_cycle })
1372
+ ] })
1373
+ ] }),
1374
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
1375
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
1376
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground", children: "Periodo" }),
1377
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 mt-1", children: [
1378
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.Calendar, { className: "w-4 h-4 text-muted-foreground" }),
1379
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
1380
+ (0, import_billing_admin_core5.formatDate)(selectedSub.current_period_start),
1381
+ " - ",
1382
+ (0, import_billing_admin_core5.formatDate)(selectedSub.current_period_end)
1383
+ ] })
1384
+ ] })
1385
+ ] }),
1386
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
1387
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground", children: "Fine Trial" }),
1388
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "mt-1", children: (0, import_billing_admin_core5.formatDate)(selectedSub.trial_end) })
1389
+ ] })
1390
+ ] }),
1391
+ selectedSub.canceled_at && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "p-3 bg-destructive/10 rounded-lg", children: [
1392
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-sm text-destructive font-medium", children: [
1393
+ "Cancellato il ",
1394
+ (0, import_billing_admin_core5.formatDate)(selectedSub.canceled_at)
1395
+ ] }),
1396
+ selectedSub.cancel_at && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-xs text-destructive/80 mt-1", children: [
1397
+ "Termina il ",
1398
+ (0, import_billing_admin_core5.formatDate)(selectedSub.cancel_at)
1399
+ ] })
1400
+ ] }),
1401
+ selectedSub.stripe_subscription_id && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
1402
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground", children: "Stripe ID" }),
1403
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("code", { className: "text-xs bg-muted px-2 py-1 rounded mt-1 inline-block", children: selectedSub.stripe_subscription_id })
1404
+ ] })
1405
+ ] }),
1406
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex justify-end pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent", onClick: () => setShowDetails(false), children: "Chiudi" }) })
1407
+ ] })
1408
+ ] }),
1409
+ showCancelDialog && selectedSub && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
1410
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => setShowCancelDialog(false) }),
1411
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "relative z-50 w-full max-w-lg rounded-lg border bg-background p-6 shadow-lg", children: [
1412
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { className: "text-lg font-semibold", children: "Cancellare Abbonamento?" }),
1413
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-sm text-muted-foreground mt-1", children: [
1414
+ "Stai per cancellare l'abbonamento di ",
1415
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: selectedSub.subscriber_email }),
1416
+ "."
1417
+ ] }),
1418
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "py-4", children: [
1419
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer", children: [
1420
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { type: "checkbox", checked: cancelAtPeriodEnd, onChange: (e) => setCancelAtPeriodEnd(e.target.checked), className: "rounded border-input" }),
1421
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-sm", children: "Cancella alla fine del periodo corrente (consigliato)" })
1422
+ ] }),
1423
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs text-muted-foreground mt-2", children: cancelAtPeriodEnd ? "L'abbonamento rimarr\xE0 attivo fino alla fine del periodo." : "L'abbonamento verr\xE0 cancellato immediatamente." })
1424
+ ] }),
1425
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex justify-end gap-2", children: [
1426
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent", onClick: () => setShowCancelDialog(false), children: "Annulla" }),
1427
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-destructive text-destructive-foreground hover:bg-destructive/90 disabled:opacity-50", onClick: handleCancel, disabled: cancelMutation.isPending, children: cancelMutation.isPending ? "Cancellazione..." : "Conferma Cancellazione" })
1428
+ ] })
1429
+ ] })
1430
+ ] })
1431
+ ] });
1432
+ return Wrapper ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Wrapper, { children: content }) : content;
1433
+ }
1434
+
1435
+ // src/pages/PaymentsPage.tsx
1436
+ var import_react6 = require("react");
1437
+ var import_sonner4 = require("sonner");
1438
+ var import_lucide_react10 = require("lucide-react");
1439
+ var import_billing_admin_core6 = require("@growflowstudio/billing-admin-core");
1440
+
1441
+ // src/components/shared/CursorPagination.tsx
1442
+ var import_lucide_react9 = require("lucide-react");
1443
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1444
+ function CursorPagination({
1445
+ cursor,
1446
+ hasMore,
1447
+ items,
1448
+ onFirstPage,
1449
+ onNextPage
1450
+ }) {
1451
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex justify-between items-center", children: [
1452
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1453
+ "button",
1454
+ {
1455
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-3 border border-input bg-background hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:pointer-events-none",
1456
+ onClick: onFirstPage,
1457
+ disabled: !cursor,
1458
+ children: [
1459
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.ChevronLeft, { className: "w-4 h-4 mr-1" }),
1460
+ "Prima Pagina"
1461
+ ]
1462
+ }
1463
+ ),
1464
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1465
+ "button",
1466
+ {
1467
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-3 border border-input bg-background hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:pointer-events-none",
1468
+ onClick: () => {
1469
+ if (items.length > 0) {
1470
+ onNextPage(items[items.length - 1].id);
1471
+ }
1472
+ },
1473
+ disabled: !hasMore,
1474
+ children: [
1475
+ "Prossima",
1476
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.ChevronRight, { className: "w-4 h-4 ml-1" })
1477
+ ]
1478
+ }
1479
+ )
1480
+ ] });
1481
+ }
1482
+
1483
+ // src/pages/PaymentsPage.tsx
1484
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1485
+ var statusFilterOptions2 = [
1486
+ { value: "all", label: "Tutti gli stati" },
1487
+ { value: "succeeded", label: "Completati" },
1488
+ { value: "pending", label: "In Attesa" },
1489
+ { value: "failed", label: "Falliti" },
1490
+ { value: "canceled", label: "Annullati" }
1491
+ ];
1492
+ function PaymentsPage({ wrapper: Wrapper, header }) {
1493
+ const [search, setSearch] = (0, import_react6.useState)("");
1494
+ const [statusFilter, setStatusFilter] = (0, import_react6.useState)("all");
1495
+ const [cursor, setCursor] = (0, import_react6.useState)();
1496
+ const [selectedPayment, setSelectedPayment] = (0, import_react6.useState)(null);
1497
+ const [showRefundDialog, setShowRefundDialog] = (0, import_react6.useState)(false);
1498
+ const [refundAmount, setRefundAmount] = (0, import_react6.useState)("");
1499
+ const [refundReason, setRefundReason] = (0, import_react6.useState)("requested_by_customer");
1500
+ const { data, isLoading, refetch } = (0, import_billing_admin_core6.useAdminPayments)({
1501
+ search: search || void 0,
1502
+ status: statusFilter !== "all" ? statusFilter : void 0,
1503
+ limit: 25,
1504
+ starting_after: cursor
1505
+ });
1506
+ const refundMutation = (0, import_billing_admin_core6.useRefundPayment)();
1507
+ const payments = data?.data || [];
1508
+ const hasMore = data?.has_more || false;
1509
+ const handleRefund = () => {
1510
+ if (!selectedPayment) return;
1511
+ const amountCents = refundAmount ? Math.round(parseFloat(refundAmount) * 100) : void 0;
1512
+ refundMutation.mutate({ id: selectedPayment.id, amount: amountCents, reason: refundReason }, {
1513
+ onSuccess: () => {
1514
+ import_sonner4.toast.success("Rimborso effettuato");
1515
+ setShowRefundDialog(false);
1516
+ setSelectedPayment(null);
1517
+ setRefundAmount("");
1518
+ },
1519
+ onError: (err) => import_sonner4.toast.error(`Errore rimborso: ${err.message}`)
1520
+ });
1521
+ };
1522
+ const openRefundDialog = (payment) => {
1523
+ setSelectedPayment(payment);
1524
+ setRefundAmount((payment.amount / 100).toFixed(2));
1525
+ setShowRefundDialog(true);
1526
+ };
1527
+ const content = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
1528
+ header,
1529
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "space-y-4", children: [
1530
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1531
+ FilterBar,
1532
+ {
1533
+ searchPlaceholder: "Cerca per email cliente...",
1534
+ searchValue: search,
1535
+ onSearchChange: (v) => {
1536
+ setSearch(v);
1537
+ setCursor(void 0);
1538
+ },
1539
+ statusFilter,
1540
+ onStatusFilterChange: (v) => {
1541
+ setStatusFilter(v);
1542
+ setCursor(void 0);
1543
+ },
1544
+ statusOptions: statusFilterOptions2,
1545
+ onRefresh: refetch
1546
+ }
1547
+ ),
1548
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
1549
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.Receipt, { className: "w-4 h-4" }),
1550
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { children: [
1551
+ payments.length,
1552
+ " pagamenti visualizzati"
1553
+ ] })
1554
+ ] }),
1555
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "rounded-lg border bg-card shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("table", { className: "w-full caption-bottom text-sm", children: [
1556
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("thead", { className: "[&_tr]:border-b", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
1557
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "ID Pagamento" }),
1558
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Importo" }),
1559
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Stato" }),
1560
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Cliente" }),
1561
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Descrizione" }),
1562
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Data" }),
1563
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("th", { className: "h-12 px-4 text-right font-medium text-muted-foreground", children: "Azioni" })
1564
+ ] }) }),
1565
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("tbody", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SkeletonRows, { rows: 5, columns: 7 }) : payments.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("td", { colSpan: 7, className: "p-4 text-center py-8 text-muted-foreground", children: "Nessun pagamento trovato" }) }) : payments.map((payment) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
1566
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("code", { className: "text-xs bg-muted px-2 py-0.5 rounded", children: (0, import_billing_admin_core6.truncateId)(payment.id) }) }),
1567
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("td", { className: "p-4 font-medium", children: (0, import_billing_admin_core6.formatCurrencyFromCents)(payment.amount, payment.currency) }),
1568
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(StatusBadge, { label: import_billing_admin_core6.paymentStatusLabels[payment.status], colorClass: import_billing_admin_core6.paymentStatusColors[payment.status] }) }),
1569
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("td", { className: "p-4", children: payment.customer_email || /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-muted-foreground", children: "-" }) }),
1570
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("td", { className: "p-4 max-w-[200px] truncate", children: payment.description || /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-muted-foreground", children: "-" }) }),
1571
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("td", { className: "p-4", children: (0, import_billing_admin_core6.formatTimestamp)(payment.created, true) }),
1572
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("td", { className: "p-4 text-right", children: payment.status === "succeeded" && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-3 hover:bg-accent", onClick: () => openRefundDialog(payment), children: [
1573
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.RotateCcw, { className: "w-4 h-4 mr-1" }),
1574
+ "Rimborsa"
1575
+ ] }) })
1576
+ ] }, payment.id)) })
1577
+ ] }) }) }),
1578
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CursorPagination, { cursor, hasMore, items: payments, onFirstPage: () => setCursor(void 0), onNextPage: setCursor })
1579
+ ] }),
1580
+ showRefundDialog && selectedPayment && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
1581
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => setShowRefundDialog(false) }),
1582
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "relative z-50 w-full max-w-md rounded-lg border bg-background p-6 shadow-lg", children: [
1583
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { className: "text-lg font-semibold", children: "Rimborso Pagamento" }),
1584
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground mt-1", children: "Effettua un rimborso per il pagamento selezionato" }),
1585
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "space-y-4 py-4", children: [
1586
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "p-3 bg-muted rounded-lg", children: [
1587
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex justify-between items-center", children: [
1588
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm text-muted-foreground", children: "Pagamento originale" }),
1589
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "font-medium", children: (0, import_billing_admin_core6.formatCurrencyFromCents)(selectedPayment.amount, selectedPayment.currency) })
1590
+ ] }),
1591
+ selectedPayment.customer_email && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { className: "text-sm text-muted-foreground mt-1", children: [
1592
+ "Cliente: ",
1593
+ selectedPayment.customer_email
1594
+ ] })
1595
+ ] }),
1596
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "space-y-2", children: [
1597
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("label", { className: "text-sm font-medium leading-none", children: "Importo Rimborso" }),
1598
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("input", { type: "number", step: "0.01", className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", value: refundAmount, onChange: (e) => setRefundAmount(e.target.value), placeholder: "Lascia vuoto per rimborso totale" })
1599
+ ] }),
1600
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "space-y-2", children: [
1601
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("label", { className: "text-sm font-medium leading-none", children: "Motivo" }),
1602
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("select", { className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", value: refundReason, onChange: (e) => setRefundReason(e.target.value), children: [
1603
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: "requested_by_customer", children: "Richiesto dal cliente" }),
1604
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: "duplicate", children: "Pagamento duplicato" }),
1605
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: "fraudulent", children: "Fraudolento" })
1606
+ ] })
1607
+ ] })
1608
+ ] }),
1609
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex justify-end gap-2", children: [
1610
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent", onClick: () => setShowRefundDialog(false), children: "Annulla" }),
1611
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-destructive text-destructive-foreground hover:bg-destructive/90 disabled:opacity-50", onClick: handleRefund, disabled: refundMutation.isPending, children: refundMutation.isPending ? "Rimborso in corso..." : "Conferma Rimborso" })
1612
+ ] })
1613
+ ] })
1614
+ ] })
1615
+ ] });
1616
+ return Wrapper ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Wrapper, { children: content }) : content;
1617
+ }
1618
+
1619
+ // src/pages/InvoicesPage.tsx
1620
+ var import_react7 = require("react");
1621
+ var import_sonner5 = require("sonner");
1622
+ var import_lucide_react12 = require("lucide-react");
1623
+ var import_billing_admin_core8 = require("@growflowstudio/billing-admin-core");
1624
+
1625
+ // src/components/invoices/InvoiceDetailsDialog.tsx
1626
+ var import_lucide_react11 = require("lucide-react");
1627
+ var import_billing_admin_core7 = require("@growflowstudio/billing-admin-core");
1628
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1629
+ function InvoiceDetailsDialog({ isOpen, onOpenChange, invoice }) {
1630
+ if (!isOpen || !invoice) return null;
1631
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
1632
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => onOpenChange(false) }),
1633
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "relative z-50 w-full max-w-lg rounded-lg border bg-background p-6 shadow-lg", children: [
1634
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col space-y-1.5 pb-4", children: [
1635
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h2", { className: "text-lg font-semibold", children: "Dettagli Fattura" }),
1636
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: invoice.number || "Fattura senza numero" })
1637
+ ] }),
1638
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "space-y-4", children: [
1639
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
1640
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
1641
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "Stato" }),
1642
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(StatusBadge, { label: import_billing_admin_core7.invoiceStatusLabels[invoice.status], colorClass: import_billing_admin_core7.invoiceStatusColors[invoice.status], className: "mt-1" })
1643
+ ] }),
1644
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
1645
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "Data Creazione" }),
1646
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "font-medium mt-1", children: (0, import_billing_admin_core7.formatTimestamp)(invoice.created) })
1647
+ ] })
1648
+ ] }),
1649
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
1650
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
1651
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "Importo Dovuto" }),
1652
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "font-medium mt-1", children: (0, import_billing_admin_core7.formatCurrencyFromCents)(invoice.amount_due, invoice.currency) })
1653
+ ] }),
1654
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
1655
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "Importo Pagato" }),
1656
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "font-medium mt-1", children: (0, import_billing_admin_core7.formatCurrencyFromCents)(invoice.amount_paid, invoice.currency) })
1657
+ ] })
1658
+ ] }),
1659
+ invoice.amount_remaining > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "p-3 bg-amber-50 dark:bg-amber-900/20 rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { className: "text-sm text-amber-700 dark:text-amber-400", children: [
1660
+ "Importo rimanente: ",
1661
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("strong", { children: (0, import_billing_admin_core7.formatCurrencyFromCents)(invoice.amount_remaining, invoice.currency) })
1662
+ ] }) }),
1663
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
1664
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "Cliente" }),
1665
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "font-medium mt-1", children: invoice.customer_name || "Non specificato" }),
1666
+ invoice.customer_email && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: invoice.customer_email })
1667
+ ] }),
1668
+ invoice.lines && invoice.lines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
1669
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground mb-2", children: "Voci Fattura" }),
1670
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "border rounded-lg divide-y", children: invoice.lines.map((line) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "p-3 flex justify-between items-center", children: [
1671
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
1672
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm font-medium", children: line.description }),
1673
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
1674
+ "Qt\xE0: ",
1675
+ line.quantity
1676
+ ] })
1677
+ ] }),
1678
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "font-medium", children: (0, import_billing_admin_core7.formatCurrencyFromCents)(line.amount, line.currency) })
1679
+ ] }, line.id)) })
1680
+ ] }),
1681
+ invoice.hosted_invoice_url && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1682
+ "button",
1683
+ {
1684
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent w-full",
1685
+ onClick: () => window.open(invoice.hosted_invoice_url, "_blank"),
1686
+ children: [
1687
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react11.ExternalLink, { className: "w-4 h-4 mr-2" }),
1688
+ "Apri su Stripe"
1689
+ ]
1690
+ }
1691
+ )
1692
+ ] }),
1693
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex justify-end pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent", onClick: () => onOpenChange(false), children: "Chiudi" }) })
1694
+ ] })
1695
+ ] });
1696
+ }
1697
+
1698
+ // src/pages/InvoicesPage.tsx
1699
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1700
+ var statusFilterOptions3 = [
1701
+ { value: "all", label: "Tutti gli stati" },
1702
+ { value: "draft", label: "Bozze" },
1703
+ { value: "open", label: "Aperte" },
1704
+ { value: "paid", label: "Pagate" },
1705
+ { value: "void", label: "Annullate" },
1706
+ { value: "uncollectible", label: "Non Riscuotibili" }
1707
+ ];
1708
+ function InvoicesPage({ wrapper: Wrapper, header }) {
1709
+ const [search, setSearch] = (0, import_react7.useState)("");
1710
+ const [statusFilter, setStatusFilter] = (0, import_react7.useState)("all");
1711
+ const [cursor, setCursor] = (0, import_react7.useState)();
1712
+ const detailsDialog = (0, import_billing_admin_core8.useDialogState)();
1713
+ const voidDialog = (0, import_billing_admin_core8.useDialogState)();
1714
+ const { data, isLoading, refetch } = (0, import_billing_admin_core8.useAdminInvoices)({
1715
+ search: search || void 0,
1716
+ status: statusFilter !== "all" ? statusFilter : void 0,
1717
+ limit: 25,
1718
+ starting_after: cursor
1719
+ });
1720
+ const sendMutation = (0, import_billing_admin_core8.useSendInvoice)();
1721
+ const voidMutation = (0, import_billing_admin_core8.useVoidInvoice)();
1722
+ const invoices = data?.data || [];
1723
+ const hasMore = data?.has_more || false;
1724
+ const handleVoidConfirm = () => {
1725
+ if (voidDialog.data) {
1726
+ voidMutation.mutate(voidDialog.data.id, {
1727
+ onSuccess: () => {
1728
+ import_sonner5.toast.success("Fattura annullata");
1729
+ voidDialog.close();
1730
+ },
1731
+ onError: (err) => import_sonner5.toast.error(`Errore: ${err.message}`)
1732
+ });
1733
+ }
1734
+ };
1735
+ const openInvoicePdf = (invoice) => {
1736
+ if (invoice.invoice_pdf) window.open(invoice.invoice_pdf, "_blank");
1737
+ else if (invoice.hosted_invoice_url) window.open(invoice.hosted_invoice_url, "_blank");
1738
+ };
1739
+ const content = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
1740
+ header,
1741
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "space-y-4", children: [
1742
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1743
+ FilterBar,
1744
+ {
1745
+ searchPlaceholder: "Cerca per email o numero fattura...",
1746
+ searchValue: search,
1747
+ onSearchChange: (v) => {
1748
+ setSearch(v);
1749
+ setCursor(void 0);
1750
+ },
1751
+ statusFilter,
1752
+ onStatusFilterChange: (v) => {
1753
+ setStatusFilter(v);
1754
+ setCursor(void 0);
1755
+ },
1756
+ statusOptions: statusFilterOptions3,
1757
+ onRefresh: refetch
1758
+ }
1759
+ ),
1760
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
1761
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react12.FileText, { className: "w-4 h-4" }),
1762
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { children: [
1763
+ invoices.length,
1764
+ " fatture visualizzate"
1765
+ ] })
1766
+ ] }),
1767
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "rounded-lg border bg-card shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("table", { className: "w-full caption-bottom text-sm", children: [
1768
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("thead", { className: "[&_tr]:border-b", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
1769
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Numero" }),
1770
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Stato" }),
1771
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Importo Dovuto" }),
1772
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Importo Pagato" }),
1773
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Cliente" }),
1774
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Data" }),
1775
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Scadenza" }),
1776
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { className: "h-12 px-4 text-right font-medium text-muted-foreground", children: "Azioni" })
1777
+ ] }) }),
1778
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tbody", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SkeletonRows, { rows: 5, columns: 8 }) : invoices.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { colSpan: 8, className: "p-4 text-center py-8 text-muted-foreground", children: "Nessuna fattura trovata" }) }) : invoices.map((inv) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
1779
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "p-4 font-medium", children: inv.number || /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-muted-foreground", children: "-" }) }),
1780
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusBadge, { label: import_billing_admin_core8.invoiceStatusLabels[inv.status], colorClass: import_billing_admin_core8.invoiceStatusColors[inv.status] }) }),
1781
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "p-4 font-medium", children: (0, import_billing_admin_core8.formatCurrencyFromCents)(inv.amount_due, inv.currency) }),
1782
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "p-4", children: (0, import_billing_admin_core8.formatCurrencyFromCents)(inv.amount_paid, inv.currency) }),
1783
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col", children: [
1784
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: inv.customer_name || "-" }),
1785
+ inv.customer_email && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs text-muted-foreground", children: inv.customer_email })
1786
+ ] }) }),
1787
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "p-4", children: (0, import_billing_admin_core8.formatTimestamp)(inv.created) }),
1788
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "p-4", children: (0, import_billing_admin_core8.formatTimestamp)(inv.due_date) }),
1789
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "p-4 text-right", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex justify-end gap-1", children: [
1790
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { className: "inline-flex items-center justify-center rounded-md h-9 w-9 hover:bg-accent", onClick: () => detailsDialog.open(inv), children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react12.Eye, { className: "w-4 h-4" }) }),
1791
+ (inv.invoice_pdf || inv.hosted_invoice_url) && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { className: "inline-flex items-center justify-center rounded-md h-9 w-9 hover:bg-accent", onClick: () => openInvoicePdf(inv), children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react12.Download, { className: "w-4 h-4" }) }),
1792
+ inv.status === "open" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { className: "inline-flex items-center justify-center rounded-md h-9 w-9 hover:bg-accent", onClick: () => sendMutation.mutate(inv.id, { onSuccess: () => import_sonner5.toast.success("Fattura inviata"), onError: (err) => import_sonner5.toast.error(`Errore: ${err.message}`) }), disabled: sendMutation.isPending, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react12.Send, { className: "w-4 h-4" }) }),
1793
+ (inv.status === "draft" || inv.status === "open") && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { className: "inline-flex items-center justify-center rounded-md h-9 w-9 text-destructive hover:bg-accent", onClick: () => voidDialog.open(inv), children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react12.XCircle, { className: "w-4 h-4" }) })
1794
+ ] }) })
1795
+ ] }, inv.id)) })
1796
+ ] }) }) }),
1797
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(CursorPagination, { cursor, hasMore, items: invoices, onFirstPage: () => setCursor(void 0), onNextPage: setCursor })
1798
+ ] }),
1799
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(InvoiceDetailsDialog, { isOpen: detailsDialog.isOpen, onOpenChange: (open) => !open && detailsDialog.close(), invoice: detailsDialog.data }),
1800
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1801
+ DeleteConfirmDialog,
1802
+ {
1803
+ isOpen: voidDialog.isOpen,
1804
+ onOpenChange: (open) => !open && voidDialog.close(),
1805
+ title: "Annullare Fattura?",
1806
+ description: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
1807
+ "Stai per annullare la fattura ",
1808
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("strong", { children: voidDialog.data?.number || voidDialog.data?.id }),
1809
+ ". Una fattura annullata non pu\xF2 essere riattivata."
1810
+ ] }),
1811
+ onConfirm: handleVoidConfirm,
1812
+ isDeleting: voidMutation.isPending,
1813
+ confirmLabel: "Conferma Annullamento"
1814
+ }
1815
+ )
1816
+ ] });
1817
+ return Wrapper ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Wrapper, { children: content }) : content;
1818
+ }
1819
+
1820
+ // src/pages/FeaturesPage.tsx
1821
+ var import_react8 = require("react");
1822
+ var import_sonner6 = require("sonner");
1823
+ var import_lucide_react13 = require("lucide-react");
1824
+ var import_billing_admin_core9 = require("@growflowstudio/billing-admin-core");
1825
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1826
+ function FeaturesPage({ wrapper: Wrapper, header }) {
1827
+ const [isCreateDialogOpen, setIsCreateDialogOpen] = (0, import_react8.useState)(false);
1828
+ const [editingFeature, setEditingFeature] = (0, import_react8.useState)(null);
1829
+ const [deletingFeature, setDeletingFeature] = (0, import_react8.useState)(null);
1830
+ const { data, isLoading } = (0, import_billing_admin_core9.useAdminFeatures)();
1831
+ const createMutation = (0, import_billing_admin_core9.useCreateFeature)();
1832
+ const updateMutation = (0, import_billing_admin_core9.useUpdateFeature)();
1833
+ const deleteMutation = (0, import_billing_admin_core9.useDeleteFeature)();
1834
+ const features = data?.features || [];
1835
+ const handleCreateFeature = (e) => {
1836
+ e.preventDefault();
1837
+ const formData = new FormData(e.currentTarget);
1838
+ const createData = {
1839
+ slug: formData.get("slug"),
1840
+ name: formData.get("name"),
1841
+ description: formData.get("description") || void 0,
1842
+ icon: formData.get("icon") || void 0,
1843
+ category: formData.get("category") || void 0,
1844
+ is_available: formData.get("is_available") === "on",
1845
+ billing_product_id: formData.get("billing_product_id") || void 0
1846
+ };
1847
+ createMutation.mutate(createData, {
1848
+ onSuccess: () => {
1849
+ import_sonner6.toast.success("Feature creata con successo");
1850
+ setIsCreateDialogOpen(false);
1851
+ },
1852
+ onError: (err) => import_sonner6.toast.error(`Errore: ${err.message}`)
1853
+ });
1854
+ };
1855
+ const handleUpdateFeature = (e) => {
1856
+ e.preventDefault();
1857
+ if (!editingFeature) return;
1858
+ const formData = new FormData(e.currentTarget);
1859
+ const updateData = {
1860
+ name: formData.get("name"),
1861
+ description: formData.get("description") || void 0,
1862
+ icon: formData.get("icon") || void 0,
1863
+ category: formData.get("category") || void 0,
1864
+ is_available: formData.get("is_available") === "on",
1865
+ billing_product_id: formData.get("billing_product_id") || void 0
1866
+ };
1867
+ updateMutation.mutate({ id: editingFeature.id, data: updateData }, {
1868
+ onSuccess: () => {
1869
+ import_sonner6.toast.success("Feature aggiornata con successo");
1870
+ setEditingFeature(null);
1871
+ },
1872
+ onError: (err) => import_sonner6.toast.error(`Errore: ${err.message}`)
1873
+ });
1874
+ };
1875
+ const handleDeleteConfirm = () => {
1876
+ if (deletingFeature) {
1877
+ deleteMutation.mutate(deletingFeature.id, {
1878
+ onSuccess: () => {
1879
+ import_sonner6.toast.success("Feature eliminata con successo");
1880
+ setDeletingFeature(null);
1881
+ },
1882
+ onError: (err) => import_sonner6.toast.error(`Errore: ${err.message}`)
1883
+ });
1884
+ }
1885
+ };
1886
+ const content = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
1887
+ header,
1888
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-4", children: [
1889
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between", children: [
1890
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
1891
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react13.Package, { className: "w-4 h-4" }),
1892
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { children: [
1893
+ features.length,
1894
+ " feature",
1895
+ features.length !== 1 ? "s" : "",
1896
+ " configurate"
1897
+ ] })
1898
+ ] }),
1899
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1900
+ "button",
1901
+ {
1902
+ className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90",
1903
+ onClick: () => setIsCreateDialogOpen(true),
1904
+ children: [
1905
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react13.Plus, { className: "w-4 h-4 mr-2" }),
1906
+ "Nuova Feature"
1907
+ ]
1908
+ }
1909
+ )
1910
+ ] }),
1911
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "rounded-lg border bg-card shadow-sm", children: [
1912
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "p-4 border-b", children: [
1913
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("h3", { className: "text-base font-semibold flex items-center gap-2", children: [
1914
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react13.Package, { className: "w-5 h-5" }),
1915
+ "Features Disponibili"
1916
+ ] }),
1917
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm text-muted-foreground mt-1", children: "Le features possono essere collegate a prodotti billing o attivate manualmente." })
1918
+ ] }),
1919
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "p-4", children: [
1920
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "space-y-3", children: [1, 2, 3].map((i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "h-20 bg-muted animate-pulse rounded-lg" }, i)) }) : features.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "text-center py-12 text-muted-foreground", children: [
1921
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react13.Package, { className: "mx-auto h-12 w-12 mb-4 opacity-50" }),
1922
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { children: "Nessuna feature configurata" }),
1923
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1924
+ "button",
1925
+ {
1926
+ className: "text-sm text-primary hover:underline mt-2",
1927
+ onClick: () => setIsCreateDialogOpen(true),
1928
+ children: "Crea la prima feature"
1929
+ }
1930
+ )
1931
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "space-y-3", children: features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1932
+ "div",
1933
+ {
1934
+ className: "border rounded-lg p-4 hover:bg-muted/50 transition-colors",
1935
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-start justify-between", children: [
1936
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex-1", children: [
1937
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [
1938
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "font-semibold", children: feature.name }),
1939
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("code", { className: "text-xs bg-muted px-2 py-0.5 rounded", children: feature.slug }),
1940
+ !feature.is_available && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(StatusBadge, { label: "Disattivata", colorClass: "bg-secondary text-secondary-foreground border-transparent" }),
1941
+ feature.billing_product_id && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(StatusBadge, { label: "Con Billing", colorClass: "border" })
1942
+ ] }),
1943
+ feature.description && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm text-muted-foreground mb-2", children: feature.description }),
1944
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-4 text-xs text-muted-foreground", children: [
1945
+ feature.category && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { children: [
1946
+ "Categoria: ",
1947
+ feature.category
1948
+ ] }),
1949
+ feature.icon && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { children: [
1950
+ "Icona: ",
1951
+ feature.icon
1952
+ ] })
1953
+ ] })
1954
+ ] }),
1955
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-1", children: [
1956
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1957
+ "button",
1958
+ {
1959
+ className: "inline-flex items-center justify-center rounded-md h-9 w-9 hover:bg-accent",
1960
+ onClick: () => setEditingFeature(feature),
1961
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react13.Pencil, { className: "w-4 h-4" })
1962
+ }
1963
+ ),
1964
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1965
+ "button",
1966
+ {
1967
+ className: "inline-flex items-center justify-center rounded-md h-9 w-9 text-destructive hover:bg-accent",
1968
+ onClick: () => setDeletingFeature(feature),
1969
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react13.Trash2, { className: "w-4 h-4" })
1970
+ }
1971
+ )
1972
+ ] })
1973
+ ] })
1974
+ },
1975
+ feature.id
1976
+ )) }),
1977
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "mt-6 flex gap-3 items-start rounded-lg border p-3 text-sm", children: [
1978
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react13.AlertCircle, { className: "w-4 h-4 mt-0.5 text-muted-foreground shrink-0" }),
1979
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "text-muted-foreground text-xs", children: [
1980
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "Nota:" }),
1981
+ " Features con un billing_product_id vengono attivate automaticamente quando il cliente sottoscrive il piano corrispondente."
1982
+ ] })
1983
+ ] })
1984
+ ] })
1985
+ ] })
1986
+ ] }),
1987
+ isCreateDialogOpen && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
1988
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => setIsCreateDialogOpen(false) }),
1989
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "relative z-50 w-full max-w-md rounded-lg border bg-background p-6 shadow-lg", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("form", { onSubmit: handleCreateFeature, children: [
1990
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("h2", { className: "text-lg font-semibold", children: "Crea Nuova Feature" }),
1991
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm text-muted-foreground mt-1", children: "Configura una nuova feature disponibile." }),
1992
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-4 py-4", children: [
1993
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-2", children: [
1994
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "slug", children: "Slug *" }),
1995
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { id: "slug", name: "slug", required: true, placeholder: "ottimizza-dati-prodotti", className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" }),
1996
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-xs text-muted-foreground", children: "Identificatore univoco (lowercase, trattini)" })
1997
+ ] }),
1998
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-2", children: [
1999
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "name", children: "Nome *" }),
2000
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { id: "name", name: "name", required: true, placeholder: "Ottimizza Dati Prodotti", className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" })
2001
+ ] }),
2002
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-2", children: [
2003
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "description", children: "Descrizione" }),
2004
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("textarea", { id: "description", name: "description", rows: 3, placeholder: "Descrizione della feature...", className: "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" })
2005
+ ] }),
2006
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
2007
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-2", children: [
2008
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "icon", children: "Icona Lucide" }),
2009
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { id: "icon", name: "icon", placeholder: "Package", className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" })
2010
+ ] }),
2011
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-2", children: [
2012
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "category", children: "Categoria" }),
2013
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { id: "category", name: "category", placeholder: "ai, limits, integration...", className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" })
2014
+ ] })
2015
+ ] }),
2016
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-2", children: [
2017
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "billing_product_id", children: "Billing Product ID (opzionale)" }),
2018
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { id: "billing_product_id", name: "billing_product_id", placeholder: "UUID del prodotto billing", className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" }),
2019
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-xs text-muted-foreground", children: "Se collegato a un prodotto, la feature viene attivata automaticamente" })
2020
+ ] }),
2021
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-2", children: [
2022
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { type: "checkbox", id: "is_available", name: "is_available", defaultChecked: true, className: "rounded border-input" }),
2023
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "is_available", children: "Feature disponibile" })
2024
+ ] })
2025
+ ] }),
2026
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex justify-end gap-2", children: [
2027
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent", onClick: () => setIsCreateDialogOpen(false), children: "Annulla" }),
2028
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "submit", className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50", disabled: createMutation.isPending, children: createMutation.isPending ? "Creazione..." : "Crea Feature" })
2029
+ ] })
2030
+ ] }) })
2031
+ ] }),
2032
+ editingFeature && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
2033
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => setEditingFeature(null) }),
2034
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "relative z-50 w-full max-w-md rounded-lg border bg-background p-6 shadow-lg", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("form", { onSubmit: handleUpdateFeature, children: [
2035
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("h2", { className: "text-lg font-semibold", children: "Modifica Feature" }),
2036
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "text-sm text-muted-foreground mt-1", children: [
2037
+ 'Aggiorna i dettagli della feature "',
2038
+ editingFeature.name,
2039
+ '".'
2040
+ ] }),
2041
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-4 py-4", children: [
2042
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-2", children: [
2043
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", children: "Slug (non modificabile)" }),
2044
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { value: editingFeature.slug, disabled: true, className: "flex h-10 w-full rounded-md border border-input bg-muted px-3 py-2 text-sm" })
2045
+ ] }),
2046
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-2", children: [
2047
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "edit-name", children: "Nome *" }),
2048
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { id: "edit-name", name: "name", required: true, defaultValue: editingFeature.name, className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" })
2049
+ ] }),
2050
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-2", children: [
2051
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "edit-description", children: "Descrizione" }),
2052
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("textarea", { id: "edit-description", name: "description", rows: 3, defaultValue: editingFeature.description || "", className: "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" })
2053
+ ] }),
2054
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
2055
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-2", children: [
2056
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "edit-icon", children: "Icona Lucide" }),
2057
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { id: "edit-icon", name: "icon", defaultValue: editingFeature.icon || "", className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" })
2058
+ ] }),
2059
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-2", children: [
2060
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "edit-category", children: "Categoria" }),
2061
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { id: "edit-category", name: "category", defaultValue: editingFeature.category || "", className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" })
2062
+ ] })
2063
+ ] }),
2064
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-2", children: [
2065
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "edit-billing_product_id", children: "Billing Product ID" }),
2066
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { id: "edit-billing_product_id", name: "billing_product_id", defaultValue: editingFeature.billing_product_id || "", className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" })
2067
+ ] }),
2068
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-2", children: [
2069
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { type: "checkbox", id: "edit-is_available", name: "is_available", defaultChecked: editingFeature.is_available, className: "rounded border-input" }),
2070
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "edit-is_available", children: "Feature disponibile" })
2071
+ ] })
2072
+ ] }),
2073
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex justify-end gap-2", children: [
2074
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent", onClick: () => setEditingFeature(null), children: "Annulla" }),
2075
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "submit", className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50", disabled: updateMutation.isPending, children: updateMutation.isPending ? "Salvataggio..." : "Salva Modifiche" })
2076
+ ] })
2077
+ ] }) })
2078
+ ] }),
2079
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2080
+ DeleteConfirmDialog,
2081
+ {
2082
+ isOpen: !!deletingFeature,
2083
+ onOpenChange: (open) => !open && setDeletingFeature(null),
2084
+ title: "Eliminare Feature?",
2085
+ description: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
2086
+ "Stai per eliminare la feature ",
2087
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: deletingFeature?.name }),
2088
+ " (",
2089
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("code", { children: deletingFeature?.slug }),
2090
+ "). Questa azione non pu\xF2 essere annullata."
2091
+ ] }),
2092
+ onConfirm: handleDeleteConfirm,
2093
+ isDeleting: deleteMutation.isPending,
2094
+ confirmLabel: "Elimina Feature"
2095
+ }
2096
+ )
2097
+ ] });
2098
+ return Wrapper ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Wrapper, { children: content }) : content;
2099
+ }
2100
+
2101
+ // src/nav/billingNavItems.ts
2102
+ var ALL_NAV_ITEMS = [
2103
+ { key: "plans", label: "Piani", icon: "LayoutGrid", href: "/admin/plans" },
2104
+ { key: "subscriptions", label: "Abbonamenti", icon: "CreditCard", href: "/admin/subscriptions" },
2105
+ { key: "products", label: "Prodotti", icon: "Package", href: "/admin/products" },
2106
+ { key: "features", label: "Features", icon: "Puzzle", href: "/admin/features" },
2107
+ { key: "payments", label: "Pagamenti", icon: "Receipt", href: "/admin/payments" },
2108
+ { key: "invoices", label: "Fatture", icon: "FileText", href: "/admin/invoices" }
2109
+ ];
2110
+ function getBillingNavItems(modules, basePath) {
2111
+ const enabled = {
2112
+ plans: true,
2113
+ subscriptions: true,
2114
+ products: true,
2115
+ payments: true,
2116
+ invoices: true,
2117
+ features: true,
2118
+ ...modules
2119
+ };
2120
+ return ALL_NAV_ITEMS.filter((item) => enabled[item.key] !== false).map((item) => ({
2121
+ ...item,
2122
+ href: basePath ? `${basePath}${item.href}` : item.href
2123
+ }));
2124
+ }
2125
+ // Annotate the CommonJS export names for ESM import in node:
2126
+ 0 && (module.exports = {
2127
+ BillingAdminProvider,
2128
+ CursorPagination,
2129
+ DeleteConfirmDialog,
2130
+ FeaturesPage,
2131
+ FilterBar,
2132
+ InvoiceDetailsDialog,
2133
+ InvoicesPage,
2134
+ LimitInput,
2135
+ PLAN_COLORS,
2136
+ PaymentsPage,
2137
+ PlanFormDialog,
2138
+ PlansPage,
2139
+ PlansTable,
2140
+ ProductFormDialog,
2141
+ ProductsPage,
2142
+ SkeletonRows,
2143
+ StatusBadge,
2144
+ SubscriptionsPage,
2145
+ createDefaultPlanFormData,
2146
+ defaultProductFormData,
2147
+ getBillingNavItems,
2148
+ getPlanColorClass,
2149
+ useBillingAdminConfig,
2150
+ useEnabledModules
2151
+ });
2152
+ //# sourceMappingURL=index.js.map