@growflowstudio/growflowbilling-admin-ui 2.0.1 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +90 -2
- package/dist/index.d.ts +90 -2
- package/dist/index.js +1368 -932
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1337 -911
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -33,6 +33,8 @@ __export(index_exports, {
|
|
|
33
33
|
BillingAdminProvider: () => BillingAdminProvider,
|
|
34
34
|
CursorPagination: () => CursorPagination,
|
|
35
35
|
DeleteConfirmDialog: () => DeleteConfirmDialog,
|
|
36
|
+
FeatureFormModal: () => FeatureFormModal,
|
|
37
|
+
FeatureListItem: () => FeatureListItem,
|
|
36
38
|
FeaturesPage: () => FeaturesPage,
|
|
37
39
|
FilterBar: () => FilterBar,
|
|
38
40
|
InvoiceDetailsDialog: () => InvoiceDetailsDialog,
|
|
@@ -40,7 +42,11 @@ __export(index_exports, {
|
|
|
40
42
|
LimitInput: () => LimitInput,
|
|
41
43
|
PLAN_COLORS: () => PLAN_COLORS,
|
|
42
44
|
PaymentsPage: () => PaymentsPage,
|
|
45
|
+
PlanDetailsSection: () => PlanDetailsSection,
|
|
46
|
+
PlanFeaturesSection: () => PlanFeaturesSection,
|
|
43
47
|
PlanFormDialog: () => PlanFormDialog,
|
|
48
|
+
PlanLimitsSection: () => PlanLimitsSection,
|
|
49
|
+
PlanPricingSection: () => PlanPricingSection,
|
|
44
50
|
PlansPage: () => PlansPage,
|
|
45
51
|
PlansTable: () => PlansTable,
|
|
46
52
|
ProductFormDialog: () => ProductFormDialog,
|
|
@@ -48,6 +54,7 @@ __export(index_exports, {
|
|
|
48
54
|
SkeletonRows: () => SkeletonRows,
|
|
49
55
|
StatusBadge: () => StatusBadge,
|
|
50
56
|
SubscriptionsPage: () => SubscriptionsPage,
|
|
57
|
+
ToggleSwitch: () => ToggleSwitch,
|
|
51
58
|
createDefaultPlanFormData: () => createDefaultPlanFormData,
|
|
52
59
|
defaultProductFormData: () => defaultProductFormData,
|
|
53
60
|
getBillingNavItems: () => getBillingNavItems,
|
|
@@ -58,9 +65,43 @@ __export(index_exports, {
|
|
|
58
65
|
module.exports = __toCommonJS(index_exports);
|
|
59
66
|
|
|
60
67
|
// src/provider.tsx
|
|
61
|
-
var
|
|
68
|
+
var import_react2 = __toESM(require("react"));
|
|
62
69
|
var import_growflowbilling_admin_core = require("@growflowstudio/growflowbilling-admin-core");
|
|
70
|
+
|
|
71
|
+
// src/components/shared/ErrorBoundary.tsx
|
|
72
|
+
var import_react = require("react");
|
|
63
73
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
74
|
+
var ErrorBoundary = class extends import_react.Component {
|
|
75
|
+
constructor(props) {
|
|
76
|
+
super(props);
|
|
77
|
+
this.state = { hasError: false, error: null };
|
|
78
|
+
}
|
|
79
|
+
static getDerivedStateFromError(error) {
|
|
80
|
+
return { hasError: true, error };
|
|
81
|
+
}
|
|
82
|
+
componentDidCatch(error, errorInfo) {
|
|
83
|
+
console.error("[BillingSDK] Error caught by boundary:", error, errorInfo);
|
|
84
|
+
}
|
|
85
|
+
render() {
|
|
86
|
+
if (this.state.hasError) {
|
|
87
|
+
return this.props.fallback ?? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "1rem", textAlign: "center", color: "#666" }, children: [
|
|
88
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "Something went wrong in the billing module." }),
|
|
89
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
90
|
+
"button",
|
|
91
|
+
{
|
|
92
|
+
onClick: () => this.setState({ hasError: false, error: null }),
|
|
93
|
+
style: { marginTop: "0.5rem", cursor: "pointer" },
|
|
94
|
+
children: "Try again"
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
] });
|
|
98
|
+
}
|
|
99
|
+
return this.props.children;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// src/provider.tsx
|
|
104
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
64
105
|
var DEFAULT_MODULES = {
|
|
65
106
|
plans: true,
|
|
66
107
|
subscriptions: true,
|
|
@@ -69,9 +110,9 @@ var DEFAULT_MODULES = {
|
|
|
69
110
|
invoices: true,
|
|
70
111
|
features: true
|
|
71
112
|
};
|
|
72
|
-
var BillingAdminConfigContext =
|
|
113
|
+
var BillingAdminConfigContext = import_react2.default.createContext(null);
|
|
73
114
|
function useBillingAdminConfig() {
|
|
74
|
-
const config =
|
|
115
|
+
const config = import_react2.default.useContext(BillingAdminConfigContext);
|
|
75
116
|
if (!config) {
|
|
76
117
|
throw new Error(
|
|
77
118
|
"useBillingAdminConfig must be used within a BillingAdminProvider."
|
|
@@ -84,20 +125,20 @@ function useEnabledModules() {
|
|
|
84
125
|
return { ...DEFAULT_MODULES, ...config.modules };
|
|
85
126
|
}
|
|
86
127
|
function BillingAdminProvider({ config, children }) {
|
|
87
|
-
const client = (0,
|
|
128
|
+
const client = (0, import_react2.useMemo)(
|
|
88
129
|
() => (0, import_growflowbilling_admin_core.createBillingAdminClient)({
|
|
89
130
|
basePath: config.basePath,
|
|
90
131
|
fetcher: config.fetcher
|
|
91
132
|
}),
|
|
92
133
|
[config.basePath, config.fetcher]
|
|
93
134
|
);
|
|
94
|
-
return /* @__PURE__ */ (0,
|
|
135
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BillingAdminConfigContext.Provider, { value: config, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_growflowbilling_admin_core.BillingAdminClientContext.Provider, { value: client, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ErrorBoundary, { children }) }) });
|
|
95
136
|
}
|
|
96
137
|
|
|
97
138
|
// src/pages/PlansPage.tsx
|
|
98
|
-
var
|
|
139
|
+
var import_react5 = require("react");
|
|
99
140
|
var import_sonner = require("sonner");
|
|
100
|
-
var
|
|
141
|
+
var import_lucide_react5 = require("lucide-react");
|
|
101
142
|
var import_growflowbilling_admin_core3 = require("@growflowstudio/growflowbilling-admin-core");
|
|
102
143
|
|
|
103
144
|
// src/components/plans/PlansTable.tsx
|
|
@@ -128,9 +169,9 @@ function cn(...inputs) {
|
|
|
128
169
|
}
|
|
129
170
|
|
|
130
171
|
// src/components/shared/StatusBadge.tsx
|
|
131
|
-
var
|
|
172
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
132
173
|
function StatusBadge({ label, colorClass, className }) {
|
|
133
|
-
return /* @__PURE__ */ (0,
|
|
174
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
134
175
|
"span",
|
|
135
176
|
{
|
|
136
177
|
className: cn(
|
|
@@ -144,13 +185,13 @@ function StatusBadge({ label, colorClass, className }) {
|
|
|
144
185
|
}
|
|
145
186
|
|
|
146
187
|
// src/components/shared/SkeletonRows.tsx
|
|
147
|
-
var
|
|
188
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
148
189
|
function SkeletonRows({ rows = 5, columns = 1 }) {
|
|
149
|
-
return /* @__PURE__ */ (0,
|
|
190
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { colSpan: columns, className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "h-10 w-full animate-pulse rounded-md bg-muted" }) }) }, i)) });
|
|
150
191
|
}
|
|
151
192
|
|
|
152
193
|
// src/components/plans/PlansTable.tsx
|
|
153
|
-
var
|
|
194
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
154
195
|
var pricingModelLabels = {
|
|
155
196
|
flat: "Fisso",
|
|
156
197
|
tiered: "A Scaglioni",
|
|
@@ -165,55 +206,55 @@ function PlansTable({
|
|
|
165
206
|
onSync,
|
|
166
207
|
isSyncing
|
|
167
208
|
}) {
|
|
168
|
-
return /* @__PURE__ */ (0,
|
|
169
|
-
/* @__PURE__ */ (0,
|
|
170
|
-
/* @__PURE__ */ (0,
|
|
171
|
-
/* @__PURE__ */ (0,
|
|
172
|
-
/* @__PURE__ */ (0,
|
|
173
|
-
/* @__PURE__ */ (0,
|
|
174
|
-
/* @__PURE__ */ (0,
|
|
175
|
-
/* @__PURE__ */ (0,
|
|
176
|
-
/* @__PURE__ */ (0,
|
|
177
|
-
/* @__PURE__ */ (0,
|
|
178
|
-
/* @__PURE__ */ (0,
|
|
179
|
-
/* @__PURE__ */ (0,
|
|
209
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("table", { className: "w-full caption-bottom text-sm", children: [
|
|
210
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("thead", { className: "[&_tr]:border-b", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { className: "border-b transition-colors hover:bg-muted/50", children: [
|
|
211
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Slug" }),
|
|
212
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Nome" }),
|
|
213
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Colore" }),
|
|
214
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Modello" }),
|
|
215
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Prezzo Mensile" }),
|
|
216
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Prezzo Annuale" }),
|
|
217
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Trial" }),
|
|
218
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Attivo" }),
|
|
219
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Stripe" }),
|
|
220
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { className: "h-12 px-4 text-right align-middle font-medium text-muted-foreground", children: "Azioni" })
|
|
180
221
|
] }) }),
|
|
181
|
-
/* @__PURE__ */ (0,
|
|
182
|
-
/* @__PURE__ */ (0,
|
|
183
|
-
/* @__PURE__ */ (0,
|
|
184
|
-
/* @__PURE__ */ (0,
|
|
185
|
-
/* @__PURE__ */ (0,
|
|
186
|
-
/* @__PURE__ */ (0,
|
|
187
|
-
/* @__PURE__ */ (0,
|
|
188
|
-
/* @__PURE__ */ (0,
|
|
222
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("tbody", { className: "[&_tr:last-child]:border-0", children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SkeletonRows, { rows: 3, columns: 10 }) : plans.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime5.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_runtime5.jsxs)("tr", { className: "border-b transition-colors hover:bg-muted/50", children: [
|
|
223
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "p-4 align-middle", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { className: "text-sm bg-muted px-2 py-0.5 rounded", children: plan.slug }) }),
|
|
224
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "p-4 align-middle font-medium", children: plan.name }),
|
|
225
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "p-4 align-middle", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatusBadge, { label: plan.color, colorClass: getPlanColorClass(plan.color) }) }),
|
|
226
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "p-4 align-middle", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatusBadge, { label: pricingModelLabels[plan.pricing_model], colorClass: "border" }) }),
|
|
227
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "p-4 align-middle", children: plan.contact_sales ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-muted-foreground", children: "Contattaci" }) : (0, import_growflowbilling_admin_core2.formatCurrency)(plan.price_monthly ?? 0, plan.currency) }),
|
|
228
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "p-4 align-middle", children: plan.contact_sales ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-muted-foreground", children: "Contattaci" }) : (0, import_growflowbilling_admin_core2.formatCurrency)(plan.price_yearly ?? 0, plan.currency) }),
|
|
229
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("td", { className: "p-4 align-middle", children: [
|
|
189
230
|
plan.trial_days,
|
|
190
231
|
" giorni"
|
|
191
232
|
] }),
|
|
192
|
-
/* @__PURE__ */ (0,
|
|
193
|
-
/* @__PURE__ */ (0,
|
|
233
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "p-4 align-middle", children: plan.is_active ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.Check, { className: "w-4 h-4 text-green-600" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.X, { className: "w-4 h-4 text-muted-foreground" }) }),
|
|
234
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "p-4 align-middle", children: plan.stripe_price_id_monthly ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatusBadge, { label: "Synced", colorClass: "bg-green-50 text-green-700 border-green-200" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
194
235
|
"button",
|
|
195
236
|
{
|
|
196
237
|
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
238
|
onClick: () => onSync(plan.id),
|
|
198
239
|
disabled: isSyncing,
|
|
199
|
-
children: /* @__PURE__ */ (0,
|
|
240
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.Upload, { className: "w-4 h-4" })
|
|
200
241
|
}
|
|
201
242
|
) }),
|
|
202
|
-
/* @__PURE__ */ (0,
|
|
203
|
-
/* @__PURE__ */ (0,
|
|
243
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "p-4 align-middle text-right", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-end gap-1", children: [
|
|
244
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
204
245
|
"button",
|
|
205
246
|
{
|
|
206
247
|
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
248
|
onClick: () => onEdit(plan),
|
|
208
|
-
children: /* @__PURE__ */ (0,
|
|
249
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.Pencil, { className: "w-4 h-4" })
|
|
209
250
|
}
|
|
210
251
|
),
|
|
211
|
-
/* @__PURE__ */ (0,
|
|
252
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
212
253
|
"button",
|
|
213
254
|
{
|
|
214
255
|
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
256
|
onClick: () => onDelete(plan),
|
|
216
|
-
children: /* @__PURE__ */ (0,
|
|
257
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.Trash2, { className: "w-4 h-4" })
|
|
217
258
|
}
|
|
218
259
|
)
|
|
219
260
|
] }) })
|
|
@@ -222,12 +263,200 @@ function PlansTable({
|
|
|
222
263
|
}
|
|
223
264
|
|
|
224
265
|
// src/components/plans/PlanFormDialog.tsx
|
|
225
|
-
var
|
|
266
|
+
var import_react4 = require("react");
|
|
226
267
|
var import_react_query = require("@tanstack/react-query");
|
|
227
|
-
var
|
|
268
|
+
var import_lucide_react3 = require("lucide-react");
|
|
269
|
+
|
|
270
|
+
// src/components/shared/ToggleSwitch.tsx
|
|
271
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
272
|
+
function ToggleSwitch({ label, checked, onChange }) {
|
|
273
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
274
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
275
|
+
"button",
|
|
276
|
+
{
|
|
277
|
+
type: "button",
|
|
278
|
+
role: "switch",
|
|
279
|
+
"aria-checked": checked,
|
|
280
|
+
onClick: () => onChange(!checked),
|
|
281
|
+
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"}`,
|
|
282
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
283
|
+
"span",
|
|
284
|
+
{
|
|
285
|
+
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"}`
|
|
286
|
+
}
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
),
|
|
290
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-sm font-medium leading-none", children: label })
|
|
291
|
+
] });
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/components/plans/PlanDetailsSection.tsx
|
|
295
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
296
|
+
function PlanDetailsSection({
|
|
297
|
+
formData,
|
|
298
|
+
formErrors,
|
|
299
|
+
onFormDataChange,
|
|
300
|
+
onNameChange
|
|
301
|
+
}) {
|
|
302
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
303
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-2", children: [
|
|
304
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "name", children: "Nome *" }),
|
|
305
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
306
|
+
"input",
|
|
307
|
+
{
|
|
308
|
+
id: "name",
|
|
309
|
+
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",
|
|
310
|
+
value: formData.name,
|
|
311
|
+
onChange: (e) => onNameChange(e.target.value),
|
|
312
|
+
placeholder: "es. Piano Pro"
|
|
313
|
+
}
|
|
314
|
+
),
|
|
315
|
+
formErrors.name && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-xs text-destructive", children: formErrors.name })
|
|
316
|
+
] }),
|
|
317
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-2", children: [
|
|
318
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "description", children: "Descrizione" }),
|
|
319
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
320
|
+
"textarea",
|
|
321
|
+
{
|
|
322
|
+
id: "description",
|
|
323
|
+
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",
|
|
324
|
+
value: formData.description,
|
|
325
|
+
onChange: (e) => onFormDataChange({ description: e.target.value }),
|
|
326
|
+
placeholder: "Descrizione del piano...",
|
|
327
|
+
rows: 2
|
|
328
|
+
}
|
|
329
|
+
)
|
|
330
|
+
] }),
|
|
331
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
332
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-2", children: [
|
|
333
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "text-sm font-medium leading-none", children: "Colore Badge" }),
|
|
334
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
335
|
+
"select",
|
|
336
|
+
{
|
|
337
|
+
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",
|
|
338
|
+
value: formData.color,
|
|
339
|
+
onChange: (e) => onFormDataChange({ color: e.target.value }),
|
|
340
|
+
children: PLAN_COLORS.map((color) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: color.value, children: color.label }, color.value))
|
|
341
|
+
}
|
|
342
|
+
),
|
|
343
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
344
|
+
"Anteprima:",
|
|
345
|
+
" ",
|
|
346
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.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" })
|
|
347
|
+
] })
|
|
348
|
+
] }),
|
|
349
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-4 pt-6", children: [
|
|
350
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ToggleSwitch, { label: "Piano Attivo", checked: formData.is_active, onChange: (v) => onFormDataChange({ is_active: v }) }),
|
|
351
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ToggleSwitch, { label: "Contattaci per Prezzi", checked: formData.contact_sales, onChange: (v) => onFormDataChange({ contact_sales: v }) }),
|
|
352
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ToggleSwitch, { label: "Visibile in Homepage", checked: formData.is_public, onChange: (v) => onFormDataChange({ is_public: v }) })
|
|
353
|
+
] })
|
|
354
|
+
] })
|
|
355
|
+
] });
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/components/plans/PlanPricingSection.tsx
|
|
359
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
360
|
+
function PlanPricingSection({ formData, onFormDataChange }) {
|
|
361
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
362
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "grid grid-cols-3 gap-4", children: [
|
|
363
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
|
|
364
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "text-sm font-medium leading-none", children: "Modello Pricing" }),
|
|
365
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
366
|
+
"select",
|
|
367
|
+
{
|
|
368
|
+
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",
|
|
369
|
+
value: formData.pricing_model,
|
|
370
|
+
onChange: (e) => onFormDataChange({ pricing_model: e.target.value }),
|
|
371
|
+
children: [
|
|
372
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "flat", children: "Fisso" }),
|
|
373
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "tiered", children: "A Scaglioni" }),
|
|
374
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "per_unit", children: "Per Unit\xE0" }),
|
|
375
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "usage_based", children: "A Consumo" })
|
|
376
|
+
]
|
|
377
|
+
}
|
|
378
|
+
)
|
|
379
|
+
] }),
|
|
380
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
|
|
381
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "price_monthly", children: "Prezzo Mensile" }),
|
|
382
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
383
|
+
"input",
|
|
384
|
+
{
|
|
385
|
+
id: "price_monthly",
|
|
386
|
+
type: "number",
|
|
387
|
+
step: "0.01",
|
|
388
|
+
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",
|
|
389
|
+
value: formData.price_monthly,
|
|
390
|
+
onChange: (e) => onFormDataChange({ price_monthly: e.target.value }),
|
|
391
|
+
placeholder: "0.00"
|
|
392
|
+
}
|
|
393
|
+
)
|
|
394
|
+
] }),
|
|
395
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
|
|
396
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "price_yearly", children: "Prezzo Annuale" }),
|
|
397
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
398
|
+
"input",
|
|
399
|
+
{
|
|
400
|
+
id: "price_yearly",
|
|
401
|
+
type: "number",
|
|
402
|
+
step: "0.01",
|
|
403
|
+
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",
|
|
404
|
+
value: formData.price_yearly,
|
|
405
|
+
onChange: (e) => onFormDataChange({ price_yearly: e.target.value }),
|
|
406
|
+
placeholder: "0.00"
|
|
407
|
+
}
|
|
408
|
+
)
|
|
409
|
+
] })
|
|
410
|
+
] }),
|
|
411
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "grid grid-cols-3 gap-4", children: [
|
|
412
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
|
|
413
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "text-sm font-medium leading-none", children: "Valuta" }),
|
|
414
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
415
|
+
"select",
|
|
416
|
+
{
|
|
417
|
+
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",
|
|
418
|
+
value: formData.currency,
|
|
419
|
+
onChange: (e) => onFormDataChange({ currency: e.target.value }),
|
|
420
|
+
children: [
|
|
421
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "eur", children: "EUR" }),
|
|
422
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "usd", children: "USD" }),
|
|
423
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "gbp", children: "GBP" })
|
|
424
|
+
]
|
|
425
|
+
}
|
|
426
|
+
)
|
|
427
|
+
] }),
|
|
428
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
|
|
429
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "trial_days", children: "Giorni Trial" }),
|
|
430
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
431
|
+
"input",
|
|
432
|
+
{
|
|
433
|
+
id: "trial_days",
|
|
434
|
+
type: "number",
|
|
435
|
+
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",
|
|
436
|
+
value: formData.trial_days,
|
|
437
|
+
onChange: (e) => onFormDataChange({ trial_days: parseInt(e.target.value) || 0 })
|
|
438
|
+
}
|
|
439
|
+
)
|
|
440
|
+
] }),
|
|
441
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
|
|
442
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "sort_order", children: "Ordine" }),
|
|
443
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
444
|
+
"input",
|
|
445
|
+
{
|
|
446
|
+
id: "sort_order",
|
|
447
|
+
type: "number",
|
|
448
|
+
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",
|
|
449
|
+
value: formData.sort_order,
|
|
450
|
+
onChange: (e) => onFormDataChange({ sort_order: parseInt(e.target.value) || 0 })
|
|
451
|
+
}
|
|
452
|
+
)
|
|
453
|
+
] })
|
|
454
|
+
] })
|
|
455
|
+
] });
|
|
456
|
+
}
|
|
228
457
|
|
|
229
458
|
// src/components/plans/LimitInput.tsx
|
|
230
|
-
var
|
|
459
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
231
460
|
function LimitInput({
|
|
232
461
|
id,
|
|
233
462
|
label,
|
|
@@ -237,11 +466,11 @@ function LimitInput({
|
|
|
237
466
|
onUnlimitedChange,
|
|
238
467
|
min = 1
|
|
239
468
|
}) {
|
|
240
|
-
return /* @__PURE__ */ (0,
|
|
241
|
-
/* @__PURE__ */ (0,
|
|
242
|
-
/* @__PURE__ */ (0,
|
|
243
|
-
/* @__PURE__ */ (0,
|
|
244
|
-
/* @__PURE__ */ (0,
|
|
469
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2 p-3 border rounded-lg", children: [
|
|
470
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
471
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { htmlFor: id, className: "text-sm font-medium leading-none", children: label }),
|
|
472
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
473
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
245
474
|
"button",
|
|
246
475
|
{
|
|
247
476
|
type: "button",
|
|
@@ -249,7 +478,7 @@ function LimitInput({
|
|
|
249
478
|
"aria-checked": isUnlimited,
|
|
250
479
|
onClick: () => onUnlimitedChange(!isUnlimited),
|
|
251
480
|
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,
|
|
481
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
253
482
|
"span",
|
|
254
483
|
{
|
|
255
484
|
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"}`
|
|
@@ -257,10 +486,10 @@ function LimitInput({
|
|
|
257
486
|
)
|
|
258
487
|
}
|
|
259
488
|
),
|
|
260
|
-
/* @__PURE__ */ (0,
|
|
489
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs text-muted-foreground", children: "Illimitati" })
|
|
261
490
|
] })
|
|
262
491
|
] }),
|
|
263
|
-
/* @__PURE__ */ (0,
|
|
492
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
264
493
|
"input",
|
|
265
494
|
{
|
|
266
495
|
id,
|
|
@@ -275,8 +504,275 @@ function LimitInput({
|
|
|
275
504
|
] });
|
|
276
505
|
}
|
|
277
506
|
|
|
507
|
+
// src/components/plans/PlanLimitsSection.tsx
|
|
508
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
509
|
+
function PlanLimitsSection({ limitFields, formData, onFormDataChange }) {
|
|
510
|
+
if (limitFields.length === 0) return null;
|
|
511
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-3", children: [
|
|
512
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "text-sm font-medium leading-none", children: "Limiti Piano" }),
|
|
513
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "grid grid-cols-2 gap-3", children: limitFields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
514
|
+
LimitInput,
|
|
515
|
+
{
|
|
516
|
+
id: `limit_${field.key}`,
|
|
517
|
+
label: field.label,
|
|
518
|
+
value: formData.limits[field.key] ?? field.defaultValue ?? 1,
|
|
519
|
+
isUnlimited: formData.limits_unlimited[field.key] ?? false,
|
|
520
|
+
onValueChange: (v) => onFormDataChange({ limits: { ...formData.limits, [field.key]: v } }),
|
|
521
|
+
onUnlimitedChange: (v) => onFormDataChange({ limits_unlimited: { ...formData.limits_unlimited, [field.key]: v } })
|
|
522
|
+
},
|
|
523
|
+
field.key
|
|
524
|
+
)) })
|
|
525
|
+
] });
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// src/components/plans/PlanFeaturesSection.tsx
|
|
529
|
+
var import_react3 = require("react");
|
|
530
|
+
|
|
531
|
+
// src/lib/plan-feature-state.ts
|
|
532
|
+
var FEATURE_STATE_DESCRIPTIONS = {
|
|
533
|
+
off: "Non disponibile per gli utenti di questo piano (default).",
|
|
534
|
+
teaser: "Mostrata in sidebar come locked con CTA upgrade. Non attivata funzionalmente.",
|
|
535
|
+
on: "Inclusa nel piano e pienamente accessibile.",
|
|
536
|
+
sunset: "Tecnicamente nel piano ma nascosta. Uso raro (deprecazioni / incidenti)."
|
|
537
|
+
};
|
|
538
|
+
function featureStateFromPlan(slug, features, uiVisibility) {
|
|
539
|
+
const inPlan = (features ?? []).includes(slug);
|
|
540
|
+
const ui = (uiVisibility ?? {})[slug];
|
|
541
|
+
if (inPlan) {
|
|
542
|
+
if (ui === "hidden") return "sunset";
|
|
543
|
+
return "on";
|
|
544
|
+
}
|
|
545
|
+
if (ui === "teaser") return "teaser";
|
|
546
|
+
return "off";
|
|
547
|
+
}
|
|
548
|
+
function applyFeatureState(slug, state, features, uiVisibility) {
|
|
549
|
+
const nextFeaturesSet = new Set(features ?? []);
|
|
550
|
+
const nextUi = { ...uiVisibility ?? {} };
|
|
551
|
+
switch (state) {
|
|
552
|
+
case "off":
|
|
553
|
+
nextFeaturesSet.delete(slug);
|
|
554
|
+
delete nextUi[slug];
|
|
555
|
+
break;
|
|
556
|
+
case "teaser":
|
|
557
|
+
nextFeaturesSet.delete(slug);
|
|
558
|
+
nextUi[slug] = "teaser";
|
|
559
|
+
break;
|
|
560
|
+
case "on":
|
|
561
|
+
nextFeaturesSet.add(slug);
|
|
562
|
+
delete nextUi[slug];
|
|
563
|
+
break;
|
|
564
|
+
case "sunset":
|
|
565
|
+
nextFeaturesSet.add(slug);
|
|
566
|
+
nextUi[slug] = "hidden";
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
return {
|
|
570
|
+
features: Array.from(nextFeaturesSet),
|
|
571
|
+
uiVisibility: nextUi
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
function summarizeFeatureStates(features, uiVisibility) {
|
|
575
|
+
const counts = {
|
|
576
|
+
off: 0,
|
|
577
|
+
teaser: 0,
|
|
578
|
+
on: 0,
|
|
579
|
+
sunset: 0
|
|
580
|
+
};
|
|
581
|
+
const slugs = /* @__PURE__ */ new Set([
|
|
582
|
+
...features ?? [],
|
|
583
|
+
...Object.keys(uiVisibility ?? {})
|
|
584
|
+
]);
|
|
585
|
+
for (const slug of slugs) {
|
|
586
|
+
const state = featureStateFromPlan(slug, features, uiVisibility);
|
|
587
|
+
counts[state] += 1;
|
|
588
|
+
}
|
|
589
|
+
return counts;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// src/components/plans/PlanFeatureRow.tsx
|
|
593
|
+
var import_lucide_react2 = require("lucide-react");
|
|
594
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
595
|
+
var STATE_VISUAL = {
|
|
596
|
+
off: {
|
|
597
|
+
label: "Non inclusa",
|
|
598
|
+
Icon: import_lucide_react2.CircleSlash,
|
|
599
|
+
classes: "bg-muted text-muted-foreground border-muted-foreground/20"
|
|
600
|
+
},
|
|
601
|
+
teaser: {
|
|
602
|
+
label: "Teaser",
|
|
603
|
+
Icon: import_lucide_react2.Lock,
|
|
604
|
+
classes: "bg-amber-100 text-amber-800 border-amber-300 dark:bg-amber-900/30 dark:text-amber-400 dark:border-amber-700"
|
|
605
|
+
},
|
|
606
|
+
on: {
|
|
607
|
+
label: "Inclusa",
|
|
608
|
+
Icon: import_lucide_react2.Check,
|
|
609
|
+
classes: "bg-green-100 text-green-800 border-green-300 dark:bg-green-900/30 dark:text-green-400 dark:border-green-700"
|
|
610
|
+
},
|
|
611
|
+
sunset: {
|
|
612
|
+
label: "Sunset",
|
|
613
|
+
Icon: import_lucide_react2.Archive,
|
|
614
|
+
classes: "bg-rose-100 text-rose-800 border-rose-300 dark:bg-rose-900/30 dark:text-rose-400 dark:border-rose-700"
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
function PlanFeatureRow({
|
|
618
|
+
slug,
|
|
619
|
+
name,
|
|
620
|
+
description,
|
|
621
|
+
state,
|
|
622
|
+
showSunset = false,
|
|
623
|
+
onChange,
|
|
624
|
+
onRemove
|
|
625
|
+
}) {
|
|
626
|
+
const visual = STATE_VISUAL[state];
|
|
627
|
+
const Icon = visual.Icon;
|
|
628
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-start gap-3 p-2 rounded-md hover:bg-muted/50 transition-colors", children: [
|
|
629
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
630
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2 flex-wrap", children: [
|
|
631
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-sm font-medium truncate", children: name || slug }),
|
|
632
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
633
|
+
"span",
|
|
634
|
+
{
|
|
635
|
+
className: `inline-flex items-center h-5 text-[10px] px-1.5 gap-1 rounded-md border ${visual.classes}`,
|
|
636
|
+
children: [
|
|
637
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Icon, { className: "h-3 w-3 mr-1" }),
|
|
638
|
+
visual.label
|
|
639
|
+
]
|
|
640
|
+
}
|
|
641
|
+
)
|
|
642
|
+
] }),
|
|
643
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground mt-0.5 line-clamp-1", children: description }),
|
|
644
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("code", { className: "text-[10px] text-muted-foreground bg-muted px-1.5 py-0.5 rounded mt-1 inline-block", children: slug })
|
|
645
|
+
] }),
|
|
646
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
647
|
+
"select",
|
|
648
|
+
{
|
|
649
|
+
value: state,
|
|
650
|
+
onChange: (e) => onChange(slug, e.target.value),
|
|
651
|
+
"aria-label": `Stato per ${slug}`,
|
|
652
|
+
className: "h-8 w-[140px] text-xs flex-shrink-0 rounded-md border border-input bg-background px-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
653
|
+
children: [
|
|
654
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: "off", title: FEATURE_STATE_DESCRIPTIONS.off, children: "Non inclusa" }),
|
|
655
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: "teaser", title: FEATURE_STATE_DESCRIPTIONS.teaser, children: "Teaser" }),
|
|
656
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: "on", title: FEATURE_STATE_DESCRIPTIONS.on, children: "Inclusa" }),
|
|
657
|
+
showSunset && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: "sunset", title: FEATURE_STATE_DESCRIPTIONS.sunset, children: "Sunset (admin)" })
|
|
658
|
+
]
|
|
659
|
+
}
|
|
660
|
+
),
|
|
661
|
+
onRemove && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
662
|
+
"button",
|
|
663
|
+
{
|
|
664
|
+
type: "button",
|
|
665
|
+
onClick: onRemove,
|
|
666
|
+
className: "text-muted-foreground hover:text-destructive text-xs mt-1",
|
|
667
|
+
"aria-label": `Rimuovi override ${slug}`,
|
|
668
|
+
children: "\u2715"
|
|
669
|
+
}
|
|
670
|
+
)
|
|
671
|
+
] });
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// src/components/plans/PlanFeaturesSection.tsx
|
|
675
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
676
|
+
function PlanFeaturesSection({
|
|
677
|
+
availableFeatures,
|
|
678
|
+
selectedFeatureSlugs,
|
|
679
|
+
uiVisibility,
|
|
680
|
+
onFeatureStateChange
|
|
681
|
+
}) {
|
|
682
|
+
const [showSunset, setShowSunset] = (0, import_react3.useState)(false);
|
|
683
|
+
const rowSlugs = (0, import_react3.useMemo)(() => {
|
|
684
|
+
const out = new Set(availableFeatures.map((f) => f.slug));
|
|
685
|
+
for (const slug of selectedFeatureSlugs) out.add(slug);
|
|
686
|
+
for (const slug of Object.keys(uiVisibility)) out.add(slug);
|
|
687
|
+
return Array.from(out).sort();
|
|
688
|
+
}, [availableFeatures, selectedFeatureSlugs, uiVisibility]);
|
|
689
|
+
const stateCounts = (0, import_react3.useMemo)(
|
|
690
|
+
() => summarizeFeatureStates(selectedFeatureSlugs, uiVisibility),
|
|
691
|
+
[selectedFeatureSlugs, uiVisibility]
|
|
692
|
+
);
|
|
693
|
+
const handleChange = (slug, next) => {
|
|
694
|
+
const result = applyFeatureState(
|
|
695
|
+
slug,
|
|
696
|
+
next,
|
|
697
|
+
selectedFeatureSlugs,
|
|
698
|
+
uiVisibility
|
|
699
|
+
);
|
|
700
|
+
onFeatureStateChange(result);
|
|
701
|
+
};
|
|
702
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-2", children: [
|
|
703
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center justify-between flex-wrap gap-2", children: [
|
|
704
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("label", { className: "text-sm font-medium leading-none", children: "Funzionalit\xE0 del piano" }),
|
|
705
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-1 text-xs", children: [
|
|
706
|
+
stateCounts.on > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "inline-flex items-center rounded-md border px-1.5 py-0.5 bg-green-100 text-green-800 border-green-300 dark:bg-green-900/30 dark:text-green-400 dark:border-green-700", children: [
|
|
707
|
+
stateCounts.on,
|
|
708
|
+
" inclusa",
|
|
709
|
+
stateCounts.on !== 1 ? "e" : ""
|
|
710
|
+
] }),
|
|
711
|
+
stateCounts.teaser > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "inline-flex items-center rounded-md border px-1.5 py-0.5 bg-amber-100 text-amber-800 border-amber-300 dark:bg-amber-900/30 dark:text-amber-400 dark:border-amber-700", children: [
|
|
712
|
+
stateCounts.teaser,
|
|
713
|
+
" teaser"
|
|
714
|
+
] }),
|
|
715
|
+
stateCounts.sunset > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "inline-flex items-center rounded-md border px-1.5 py-0.5 bg-rose-100 text-rose-800 border-rose-300 dark:bg-rose-900/30 dark:text-rose-400 dark:border-rose-700", children: [
|
|
716
|
+
stateCounts.sunset,
|
|
717
|
+
" sunset"
|
|
718
|
+
] })
|
|
719
|
+
] })
|
|
720
|
+
] }),
|
|
721
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
722
|
+
"Per ogni funzionalit\xE0 scegli lo stato: ",
|
|
723
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Non inclusa" }),
|
|
724
|
+
" ",
|
|
725
|
+
"(default), ",
|
|
726
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Teaser" }),
|
|
727
|
+
" (mostrata come locked con CTA upgrade), o ",
|
|
728
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Inclusa" }),
|
|
729
|
+
" (attiva). Lo stato scrive automaticamente le feature del piano e la visibility UI."
|
|
730
|
+
] }),
|
|
731
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "border rounded-lg p-2 space-y-1 max-h-[320px] overflow-y-auto", children: rowSlugs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground text-center py-4", children: "Nessuna funzionalit\xE0 disponibile nel catalogo." }) : rowSlugs.map((slug) => {
|
|
732
|
+
const feature = availableFeatures.find((f) => f.slug === slug);
|
|
733
|
+
const state = featureStateFromPlan(
|
|
734
|
+
slug,
|
|
735
|
+
selectedFeatureSlugs,
|
|
736
|
+
uiVisibility
|
|
737
|
+
);
|
|
738
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
739
|
+
PlanFeatureRow,
|
|
740
|
+
{
|
|
741
|
+
slug,
|
|
742
|
+
name: feature?.name,
|
|
743
|
+
description: feature?.description,
|
|
744
|
+
state,
|
|
745
|
+
showSunset,
|
|
746
|
+
onChange: handleChange
|
|
747
|
+
},
|
|
748
|
+
slug
|
|
749
|
+
);
|
|
750
|
+
}) }),
|
|
751
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 pt-1", children: [
|
|
752
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
753
|
+
"input",
|
|
754
|
+
{
|
|
755
|
+
id: "show-advanced-state",
|
|
756
|
+
type: "checkbox",
|
|
757
|
+
checked: showSunset,
|
|
758
|
+
onChange: (e) => setShowSunset(e.target.checked),
|
|
759
|
+
className: "h-4 w-4 rounded border-input cursor-pointer"
|
|
760
|
+
}
|
|
761
|
+
),
|
|
762
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
763
|
+
"label",
|
|
764
|
+
{
|
|
765
|
+
htmlFor: "show-advanced-state",
|
|
766
|
+
className: "text-xs text-muted-foreground cursor-pointer",
|
|
767
|
+
children: 'Mostra stato avanzato "Sunset" (feature deprecate)'
|
|
768
|
+
}
|
|
769
|
+
)
|
|
770
|
+
] })
|
|
771
|
+
] });
|
|
772
|
+
}
|
|
773
|
+
|
|
278
774
|
// src/components/plans/PlanFormDialog.tsx
|
|
279
|
-
var
|
|
775
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
280
776
|
function createDefaultPlanFormData(limitFields = []) {
|
|
281
777
|
const limits = {};
|
|
282
778
|
const limitsUnlimited = {};
|
|
@@ -300,9 +796,13 @@ function createDefaultPlanFormData(limitFields = []) {
|
|
|
300
796
|
limits,
|
|
301
797
|
limits_unlimited: limitsUnlimited,
|
|
302
798
|
features: "",
|
|
799
|
+
ui_visibility: {},
|
|
303
800
|
sort_order: 0
|
|
304
801
|
};
|
|
305
802
|
}
|
|
803
|
+
function generateSlug(name) {
|
|
804
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
805
|
+
}
|
|
306
806
|
function PlanFormDialog({
|
|
307
807
|
open,
|
|
308
808
|
onOpenChange,
|
|
@@ -316,221 +816,74 @@ function PlanFormDialog({
|
|
|
316
816
|
const config = useBillingAdminConfig();
|
|
317
817
|
const limitFields = config.plans?.limitFields || [];
|
|
318
818
|
const featuresConfig = config.plans?.features;
|
|
819
|
+
const isCreating = !selectedPlan;
|
|
319
820
|
const { data: featuresData } = (0, import_react_query.useQuery)({
|
|
320
821
|
queryKey: ["billing-admin-available-features"],
|
|
321
822
|
queryFn: () => featuresConfig?.fetchFn?.() ?? Promise.resolve({ features: [] }),
|
|
322
823
|
enabled: !!featuresConfig?.enabled && !!featuresConfig?.fetchFn
|
|
323
824
|
});
|
|
324
825
|
const availableFeatures = featuresData?.features || [];
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
826
|
+
const selectedFeatureSlugs = (0, import_react4.useMemo)(
|
|
827
|
+
() => formData.features ? formData.features.split("\n").map((line) => line.trim()).filter(Boolean) : [],
|
|
828
|
+
[formData.features]
|
|
829
|
+
);
|
|
830
|
+
const uiVisibility = (0, import_react4.useMemo)(
|
|
831
|
+
() => formData.ui_visibility ?? {},
|
|
832
|
+
[formData.ui_visibility]
|
|
833
|
+
);
|
|
834
|
+
const handleNameChange = (name) => {
|
|
835
|
+
if (isCreating) {
|
|
836
|
+
onFormDataChange({ name, slug: generateSlug(name) });
|
|
837
|
+
} else {
|
|
838
|
+
onFormDataChange({ name });
|
|
330
839
|
}
|
|
331
|
-
}
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
840
|
+
};
|
|
841
|
+
const handleFeatureStateChange = (next) => {
|
|
842
|
+
onFormDataChange({
|
|
843
|
+
features: next.features.join("\n"),
|
|
844
|
+
ui_visibility: next.uiVisibility
|
|
845
|
+
});
|
|
336
846
|
};
|
|
337
847
|
if (!open) return null;
|
|
338
|
-
return /* @__PURE__ */ (0,
|
|
339
|
-
/* @__PURE__ */ (0,
|
|
340
|
-
/* @__PURE__ */ (0,
|
|
341
|
-
/* @__PURE__ */ (0,
|
|
342
|
-
/* @__PURE__ */ (0,
|
|
343
|
-
/* @__PURE__ */ (0,
|
|
848
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
849
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => onOpenChange(false) }),
|
|
850
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
851
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col space-y-1.5 pb-4", children: [
|
|
852
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "text-lg font-semibold leading-none tracking-tight", children: selectedPlan ? "Modifica Piano" : "Nuovo Piano" }),
|
|
853
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm text-muted-foreground", children: selectedPlan ? "Modifica le impostazioni del piano selezionato" : "Crea un nuovo piano di abbonamento" })
|
|
344
854
|
] }),
|
|
345
|
-
/* @__PURE__ */ (0,
|
|
346
|
-
/* @__PURE__ */ (0,
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
-
] })
|
|
855
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "grid gap-4 py-4", children: [
|
|
856
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
857
|
+
PlanDetailsSection,
|
|
858
|
+
{
|
|
859
|
+
formData,
|
|
860
|
+
formErrors,
|
|
861
|
+
isCreating,
|
|
862
|
+
onFormDataChange,
|
|
863
|
+
onNameChange: handleNameChange
|
|
864
|
+
}
|
|
865
|
+
),
|
|
866
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PlanPricingSection, { formData, onFormDataChange }),
|
|
867
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
868
|
+
PlanLimitsSection,
|
|
869
|
+
{
|
|
870
|
+
limitFields,
|
|
871
|
+
formData,
|
|
872
|
+
onFormDataChange
|
|
873
|
+
}
|
|
874
|
+
),
|
|
875
|
+
featuresConfig?.enabled && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
876
|
+
PlanFeaturesSection,
|
|
877
|
+
{
|
|
878
|
+
availableFeatures,
|
|
879
|
+
selectedFeatureSlugs,
|
|
880
|
+
uiVisibility,
|
|
881
|
+
onFeatureStateChange: handleFeatureStateChange
|
|
882
|
+
}
|
|
883
|
+
)
|
|
531
884
|
] }),
|
|
532
|
-
/* @__PURE__ */ (0,
|
|
533
|
-
/* @__PURE__ */ (0,
|
|
885
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-4", children: [
|
|
886
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
534
887
|
"button",
|
|
535
888
|
{
|
|
536
889
|
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",
|
|
@@ -538,14 +891,14 @@ function PlanFormDialog({
|
|
|
538
891
|
children: "Annulla"
|
|
539
892
|
}
|
|
540
893
|
),
|
|
541
|
-
/* @__PURE__ */ (0,
|
|
894
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
542
895
|
"button",
|
|
543
896
|
{
|
|
544
897
|
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
898
|
onClick: onSubmit,
|
|
546
899
|
disabled: isPending,
|
|
547
900
|
children: [
|
|
548
|
-
isPending && /* @__PURE__ */ (0,
|
|
901
|
+
isPending && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.Loader2, { className: "w-4 h-4 animate-spin mr-2" }),
|
|
549
902
|
selectedPlan ? "Salva Modifiche" : "Crea Piano"
|
|
550
903
|
]
|
|
551
904
|
}
|
|
@@ -554,26 +907,10 @@ function PlanFormDialog({
|
|
|
554
907
|
] })
|
|
555
908
|
] });
|
|
556
909
|
}
|
|
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
910
|
|
|
574
911
|
// src/components/shared/DeleteConfirmDialog.tsx
|
|
575
|
-
var
|
|
576
|
-
var
|
|
912
|
+
var import_lucide_react4 = require("lucide-react");
|
|
913
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
577
914
|
function DeleteConfirmDialog({
|
|
578
915
|
isOpen,
|
|
579
916
|
onOpenChange,
|
|
@@ -585,21 +922,21 @@ function DeleteConfirmDialog({
|
|
|
585
922
|
cancelLabel = "Annulla"
|
|
586
923
|
}) {
|
|
587
924
|
if (!isOpen) return null;
|
|
588
|
-
return /* @__PURE__ */ (0,
|
|
589
|
-
/* @__PURE__ */ (0,
|
|
925
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
926
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
590
927
|
"div",
|
|
591
928
|
{
|
|
592
929
|
className: "fixed inset-0 bg-black/80",
|
|
593
930
|
onClick: () => onOpenChange(false)
|
|
594
931
|
}
|
|
595
932
|
),
|
|
596
|
-
/* @__PURE__ */ (0,
|
|
597
|
-
/* @__PURE__ */ (0,
|
|
598
|
-
/* @__PURE__ */ (0,
|
|
599
|
-
/* @__PURE__ */ (0,
|
|
933
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "relative z-50 w-full max-w-lg rounded-lg border bg-background p-6 shadow-lg", children: [
|
|
934
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col space-y-2 text-center sm:text-left", children: [
|
|
935
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { className: "text-lg font-semibold", children: title }),
|
|
936
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "text-sm text-muted-foreground", children: description })
|
|
600
937
|
] }),
|
|
601
|
-
/* @__PURE__ */ (0,
|
|
602
|
-
/* @__PURE__ */ (0,
|
|
938
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 mt-4", children: [
|
|
939
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
603
940
|
"button",
|
|
604
941
|
{
|
|
605
942
|
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",
|
|
@@ -607,14 +944,14 @@ function DeleteConfirmDialog({
|
|
|
607
944
|
children: cancelLabel
|
|
608
945
|
}
|
|
609
946
|
),
|
|
610
|
-
/* @__PURE__ */ (0,
|
|
947
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
611
948
|
"button",
|
|
612
949
|
{
|
|
613
950
|
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
951
|
onClick: onConfirm,
|
|
615
952
|
disabled: isDeleting,
|
|
616
953
|
children: [
|
|
617
|
-
isDeleting && /* @__PURE__ */ (0,
|
|
954
|
+
isDeleting && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react4.Loader2, { className: "w-4 h-4 animate-spin mr-2" }),
|
|
618
955
|
confirmLabel
|
|
619
956
|
]
|
|
620
957
|
}
|
|
@@ -625,23 +962,22 @@ function DeleteConfirmDialog({
|
|
|
625
962
|
}
|
|
626
963
|
|
|
627
964
|
// src/pages/PlansPage.tsx
|
|
628
|
-
var
|
|
965
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
629
966
|
function PlansPage({ wrapper: Wrapper, header }) {
|
|
630
967
|
const config = useBillingAdminConfig();
|
|
631
968
|
const limitFields = config.plans?.limitFields || [];
|
|
632
969
|
const publicPlansConfig = config.plans?.publicPlans;
|
|
633
|
-
const
|
|
634
|
-
const
|
|
635
|
-
const [
|
|
636
|
-
const [
|
|
637
|
-
const [formErrors, setFormErrors] = (0, import_react3.useState)({});
|
|
970
|
+
const formDialog = (0, import_growflowbilling_admin_core3.useDialogState)();
|
|
971
|
+
const deleteDialog = (0, import_growflowbilling_admin_core3.useDialogState)();
|
|
972
|
+
const [formData, setFormData] = (0, import_react5.useState)(createDefaultPlanFormData(limitFields));
|
|
973
|
+
const [formErrors, setFormErrors] = (0, import_react5.useState)({});
|
|
638
974
|
const { data, isLoading, refetch } = (0, import_growflowbilling_admin_core3.useAdminPlans)();
|
|
639
975
|
const createMutation = (0, import_growflowbilling_admin_core3.useCreatePlan)();
|
|
640
976
|
const updateMutation = (0, import_growflowbilling_admin_core3.useUpdatePlan)();
|
|
641
977
|
const deleteMutation = (0, import_growflowbilling_admin_core3.useDeletePlan)();
|
|
642
978
|
const syncMutation = (0, import_growflowbilling_admin_core3.useSyncPlanToStripe)();
|
|
643
|
-
const [publicPlanSlugs, setPublicPlanSlugs] = (0,
|
|
644
|
-
(0,
|
|
979
|
+
const [publicPlanSlugs, setPublicPlanSlugs] = (0, import_react5.useState)([]);
|
|
980
|
+
(0, import_react5.useState)(() => {
|
|
645
981
|
if (publicPlansConfig?.enabled && publicPlansConfig.fetchFn) {
|
|
646
982
|
publicPlansConfig.fetchFn().then((r) => setPublicPlanSlugs(r.public_plan_slugs));
|
|
647
983
|
}
|
|
@@ -649,15 +985,13 @@ function PlansPage({ wrapper: Wrapper, header }) {
|
|
|
649
985
|
const plans = data?.items || [];
|
|
650
986
|
const resetForm = () => {
|
|
651
987
|
setFormData(createDefaultPlanFormData(limitFields));
|
|
652
|
-
setSelectedPlan(null);
|
|
653
988
|
setFormErrors({});
|
|
654
989
|
};
|
|
655
990
|
const openCreateForm = () => {
|
|
656
991
|
resetForm();
|
|
657
|
-
|
|
992
|
+
formDialog.open(void 0);
|
|
658
993
|
};
|
|
659
994
|
const openEditForm = (plan) => {
|
|
660
|
-
setSelectedPlan(plan);
|
|
661
995
|
const planLimits = plan.limits || {};
|
|
662
996
|
const limits = {};
|
|
663
997
|
const limitsUnlimited = {};
|
|
@@ -682,27 +1016,28 @@ function PlansPage({ wrapper: Wrapper, header }) {
|
|
|
682
1016
|
limits,
|
|
683
1017
|
limits_unlimited: limitsUnlimited,
|
|
684
1018
|
features: plan.features?.join("\n") || "",
|
|
1019
|
+
ui_visibility: plan.ui_visibility ?? {},
|
|
685
1020
|
sort_order: plan.sort_order
|
|
686
1021
|
});
|
|
687
|
-
|
|
1022
|
+
formDialog.open(plan);
|
|
688
1023
|
};
|
|
689
1024
|
const handleValidate = () => {
|
|
690
1025
|
const errors = {};
|
|
691
|
-
const slugErr = (0, import_growflowbilling_admin_core3.validateSlug)(formData.slug);
|
|
692
|
-
if (slugErr) errors.slug = slugErr;
|
|
693
1026
|
const nameErr = (0, import_growflowbilling_admin_core3.validateRequired)(formData.name, "Il nome");
|
|
694
1027
|
if (nameErr) errors.name = nameErr;
|
|
695
1028
|
setFormErrors(errors);
|
|
696
1029
|
return Object.keys(errors).length === 0;
|
|
697
1030
|
};
|
|
698
1031
|
const handleSubmit = async () => {
|
|
1032
|
+
const resolvedSlug = formData.slug.trim() || formData.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
1033
|
+
const finalFormData = { ...formData, slug: resolvedSlug };
|
|
699
1034
|
if (!handleValidate()) return;
|
|
700
1035
|
const limits = {};
|
|
701
1036
|
for (const field of limitFields) {
|
|
702
|
-
limits[field.key] =
|
|
1037
|
+
limits[field.key] = finalFormData.limits_unlimited[field.key] ? -1 : finalFormData.limits[field.key] ?? 1;
|
|
703
1038
|
}
|
|
704
1039
|
const planData = {
|
|
705
|
-
slug:
|
|
1040
|
+
slug: finalFormData.slug,
|
|
706
1041
|
name: formData.name,
|
|
707
1042
|
description: formData.description || void 0,
|
|
708
1043
|
pricing_model: formData.pricing_model,
|
|
@@ -715,13 +1050,16 @@ function PlansPage({ wrapper: Wrapper, header }) {
|
|
|
715
1050
|
color: formData.color,
|
|
716
1051
|
limits: Object.keys(limits).length > 0 ? limits : void 0,
|
|
717
1052
|
features: formData.features.split("\n").filter((f) => f.trim()),
|
|
1053
|
+
// Send the full map — empty ``{}`` is meaningful (clears any
|
|
1054
|
+
// lingering overrides) so we never fall back to ``undefined``.
|
|
1055
|
+
ui_visibility: formData.ui_visibility ?? {},
|
|
718
1056
|
sort_order: formData.sort_order
|
|
719
1057
|
};
|
|
720
1058
|
if (publicPlansConfig?.enabled && publicPlansConfig.updateFn) {
|
|
721
|
-
const isCurrentlyPublic = publicPlanSlugs.includes(
|
|
722
|
-
if (
|
|
1059
|
+
const isCurrentlyPublic = publicPlanSlugs.includes(finalFormData.slug);
|
|
1060
|
+
if (finalFormData.is_public !== isCurrentlyPublic) {
|
|
723
1061
|
try {
|
|
724
|
-
const newSlugs =
|
|
1062
|
+
const newSlugs = finalFormData.is_public ? [...publicPlanSlugs, finalFormData.slug] : publicPlanSlugs.filter((s) => s !== finalFormData.slug);
|
|
725
1063
|
await publicPlansConfig.updateFn(newSlugs);
|
|
726
1064
|
setPublicPlanSlugs(newSlugs);
|
|
727
1065
|
} catch {
|
|
@@ -729,11 +1067,11 @@ function PlansPage({ wrapper: Wrapper, header }) {
|
|
|
729
1067
|
}
|
|
730
1068
|
}
|
|
731
1069
|
}
|
|
732
|
-
if (
|
|
733
|
-
updateMutation.mutate({ id:
|
|
1070
|
+
if (formDialog.data) {
|
|
1071
|
+
updateMutation.mutate({ id: formDialog.data.id, data: planData }, {
|
|
734
1072
|
onSuccess: () => {
|
|
735
1073
|
import_sonner.toast.success("Piano aggiornato con successo");
|
|
736
|
-
|
|
1074
|
+
formDialog.close();
|
|
737
1075
|
resetForm();
|
|
738
1076
|
},
|
|
739
1077
|
onError: (err) => import_sonner.toast.error(`Errore: ${err.message}`)
|
|
@@ -742,7 +1080,7 @@ function PlansPage({ wrapper: Wrapper, header }) {
|
|
|
742
1080
|
createMutation.mutate(planData, {
|
|
743
1081
|
onSuccess: () => {
|
|
744
1082
|
import_sonner.toast.success("Piano creato con successo");
|
|
745
|
-
|
|
1083
|
+
formDialog.close();
|
|
746
1084
|
resetForm();
|
|
747
1085
|
},
|
|
748
1086
|
onError: (err) => import_sonner.toast.error(`Errore: ${err.message}`)
|
|
@@ -750,49 +1088,45 @@ function PlansPage({ wrapper: Wrapper, header }) {
|
|
|
750
1088
|
}
|
|
751
1089
|
};
|
|
752
1090
|
const handleDelete = () => {
|
|
753
|
-
if (
|
|
754
|
-
deleteMutation.mutate(
|
|
1091
|
+
if (deleteDialog.data) {
|
|
1092
|
+
deleteMutation.mutate(deleteDialog.data.id, {
|
|
755
1093
|
onSuccess: () => {
|
|
756
1094
|
import_sonner.toast.success("Piano eliminato con successo");
|
|
757
|
-
|
|
758
|
-
setSelectedPlan(null);
|
|
1095
|
+
deleteDialog.close();
|
|
759
1096
|
},
|
|
760
1097
|
onError: (err) => import_sonner.toast.error(`Errore: ${err.message}`)
|
|
761
1098
|
});
|
|
762
1099
|
}
|
|
763
1100
|
};
|
|
764
|
-
const content = /* @__PURE__ */ (0,
|
|
1101
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
765
1102
|
header,
|
|
766
|
-
/* @__PURE__ */ (0,
|
|
767
|
-
/* @__PURE__ */ (0,
|
|
768
|
-
/* @__PURE__ */ (0,
|
|
769
|
-
/* @__PURE__ */ (0,
|
|
770
|
-
/* @__PURE__ */ (0,
|
|
1103
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "space-y-4", children: [
|
|
1104
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1105
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
1106
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.LayoutGrid, { className: "w-4 h-4" }),
|
|
1107
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { children: [
|
|
771
1108
|
plans.length,
|
|
772
1109
|
" piani configurati"
|
|
773
1110
|
] })
|
|
774
1111
|
] }),
|
|
775
|
-
/* @__PURE__ */ (0,
|
|
776
|
-
/* @__PURE__ */ (0,
|
|
777
|
-
/* @__PURE__ */ (0,
|
|
1112
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex gap-2", children: [
|
|
1113
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.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: [
|
|
1114
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.RefreshCw, { className: "w-4 h-4 mr-2" }),
|
|
778
1115
|
"Aggiorna"
|
|
779
1116
|
] }),
|
|
780
|
-
/* @__PURE__ */ (0,
|
|
781
|
-
/* @__PURE__ */ (0,
|
|
1117
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.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: [
|
|
1118
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Plus, { className: "w-4 h-4 mr-2" }),
|
|
782
1119
|
"Nuovo Piano"
|
|
783
1120
|
] })
|
|
784
1121
|
] })
|
|
785
1122
|
] }),
|
|
786
|
-
/* @__PURE__ */ (0,
|
|
1123
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rounded-lg border bg-card text-card-foreground shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "p-0", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
787
1124
|
PlansTable,
|
|
788
1125
|
{
|
|
789
1126
|
plans,
|
|
790
1127
|
isLoading,
|
|
791
1128
|
onEdit: openEditForm,
|
|
792
|
-
onDelete: (plan) =>
|
|
793
|
-
setSelectedPlan(plan);
|
|
794
|
-
setShowDeleteDialog(true);
|
|
795
|
-
},
|
|
1129
|
+
onDelete: (plan) => deleteDialog.open(plan),
|
|
796
1130
|
onSync: (id) => syncMutation.mutate(id, {
|
|
797
1131
|
onSuccess: () => import_sonner.toast.success("Piano sincronizzato con Stripe"),
|
|
798
1132
|
onError: (err) => import_sonner.toast.error(`Errore sync: ${err.message}`)
|
|
@@ -801,30 +1135,37 @@ function PlansPage({ wrapper: Wrapper, header }) {
|
|
|
801
1135
|
}
|
|
802
1136
|
) }) })
|
|
803
1137
|
] }),
|
|
804
|
-
/* @__PURE__ */ (0,
|
|
1138
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
805
1139
|
PlanFormDialog,
|
|
806
1140
|
{
|
|
807
|
-
open:
|
|
808
|
-
onOpenChange:
|
|
1141
|
+
open: formDialog.isOpen,
|
|
1142
|
+
onOpenChange: (open) => {
|
|
1143
|
+
if (!open) {
|
|
1144
|
+
formDialog.close();
|
|
1145
|
+
resetForm();
|
|
1146
|
+
}
|
|
1147
|
+
},
|
|
809
1148
|
formData,
|
|
810
1149
|
onFormDataChange: (data2) => setFormData((prev) => ({ ...prev, ...data2 })),
|
|
811
1150
|
formErrors,
|
|
812
|
-
selectedPlan,
|
|
1151
|
+
selectedPlan: formDialog.data ?? null,
|
|
813
1152
|
onSubmit: handleSubmit,
|
|
814
1153
|
isPending: createMutation.isPending || updateMutation.isPending
|
|
815
1154
|
}
|
|
816
1155
|
),
|
|
817
|
-
/* @__PURE__ */ (0,
|
|
1156
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
818
1157
|
DeleteConfirmDialog,
|
|
819
1158
|
{
|
|
820
|
-
isOpen:
|
|
821
|
-
onOpenChange:
|
|
1159
|
+
isOpen: deleteDialog.isOpen,
|
|
1160
|
+
onOpenChange: (open) => {
|
|
1161
|
+
if (!open) deleteDialog.close();
|
|
1162
|
+
},
|
|
822
1163
|
title: "Eliminare Piano?",
|
|
823
|
-
description: /* @__PURE__ */ (0,
|
|
1164
|
+
description: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
824
1165
|
"Stai per eliminare il piano ",
|
|
825
|
-
/* @__PURE__ */ (0,
|
|
1166
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("strong", { children: deleteDialog.data?.name }),
|
|
826
1167
|
" (",
|
|
827
|
-
/* @__PURE__ */ (0,
|
|
1168
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { children: deleteDialog.data?.slug }),
|
|
828
1169
|
"). Questa azione non pu\xF2 essere annullata."
|
|
829
1170
|
] }),
|
|
830
1171
|
onConfirm: handleDelete,
|
|
@@ -833,19 +1174,27 @@ function PlansPage({ wrapper: Wrapper, header }) {
|
|
|
833
1174
|
}
|
|
834
1175
|
)
|
|
835
1176
|
] });
|
|
836
|
-
return Wrapper ? /* @__PURE__ */ (0,
|
|
1177
|
+
return Wrapper ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Wrapper, { children: content }) : content;
|
|
837
1178
|
}
|
|
838
1179
|
|
|
839
1180
|
// src/pages/ProductsPage.tsx
|
|
840
1181
|
var import_sonner2 = require("sonner");
|
|
841
|
-
var
|
|
1182
|
+
var import_lucide_react7 = require("lucide-react");
|
|
842
1183
|
var import_react_query2 = require("@tanstack/react-query");
|
|
843
1184
|
var import_growflowbilling_admin_core4 = require("@growflowstudio/growflowbilling-admin-core");
|
|
844
1185
|
|
|
845
1186
|
// src/components/products/ProductFormDialog.tsx
|
|
846
|
-
var
|
|
847
|
-
var
|
|
848
|
-
var
|
|
1187
|
+
var import_react6 = require("react");
|
|
1188
|
+
var import_lucide_react6 = require("lucide-react");
|
|
1189
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1190
|
+
var safeJsonParse = (str, fallback = {}) => {
|
|
1191
|
+
try {
|
|
1192
|
+
const parsed = JSON.parse(str);
|
|
1193
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : fallback;
|
|
1194
|
+
} catch {
|
|
1195
|
+
return fallback;
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
849
1198
|
var defaultProductFormData = {
|
|
850
1199
|
name: "",
|
|
851
1200
|
description: "",
|
|
@@ -867,9 +1216,9 @@ function ProductFormDialog({
|
|
|
867
1216
|
onSubmit,
|
|
868
1217
|
isSubmitting
|
|
869
1218
|
}) {
|
|
870
|
-
const [formData, setFormData] = (0,
|
|
871
|
-
const [formErrors, setFormErrors] = (0,
|
|
872
|
-
(0,
|
|
1219
|
+
const [formData, setFormData] = (0, import_react6.useState)(defaultProductFormData);
|
|
1220
|
+
const [formErrors, setFormErrors] = (0, import_react6.useState)({});
|
|
1221
|
+
(0, import_react6.useEffect)(() => {
|
|
873
1222
|
if (isOpen) {
|
|
874
1223
|
if (mode === "edit" && product) {
|
|
875
1224
|
const featureSlug = product.extra_data?.feature_slug || "";
|
|
@@ -891,16 +1240,12 @@ function ProductFormDialog({
|
|
|
891
1240
|
setFormErrors({});
|
|
892
1241
|
}
|
|
893
1242
|
}, [isOpen, mode, product]);
|
|
894
|
-
(0,
|
|
1243
|
+
(0, import_react6.useEffect)(() => {
|
|
895
1244
|
if (formData.feature_slug) {
|
|
896
1245
|
setFormData((prev) => {
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
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
|
-
}
|
|
1246
|
+
const parsedData = safeJsonParse(prev.extra_data);
|
|
1247
|
+
const updated = { ...parsedData, feature_slug: formData.feature_slug };
|
|
1248
|
+
return { ...prev, extra_data: JSON.stringify(updated, null, 2) };
|
|
904
1249
|
});
|
|
905
1250
|
}
|
|
906
1251
|
}, [formData.feature_slug]);
|
|
@@ -920,72 +1265,72 @@ function ProductFormDialog({
|
|
|
920
1265
|
if (validateForm()) onSubmit(formData);
|
|
921
1266
|
};
|
|
922
1267
|
if (!isOpen) return null;
|
|
923
|
-
return /* @__PURE__ */ (0,
|
|
924
|
-
/* @__PURE__ */ (0,
|
|
925
|
-
/* @__PURE__ */ (0,
|
|
926
|
-
/* @__PURE__ */ (0,
|
|
927
|
-
/* @__PURE__ */ (0,
|
|
928
|
-
/* @__PURE__ */ (0,
|
|
1268
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
1269
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => onOpenChange(false) }),
|
|
1270
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.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: [
|
|
1271
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col space-y-1.5 pb-4", children: [
|
|
1272
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h2", { className: "text-lg font-semibold", children: mode === "create" ? "Nuovo Prodotto" : "Modifica Prodotto" }),
|
|
1273
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-sm text-muted-foreground", children: mode === "create" ? "Crea un nuovo prodotto / feature acquistabile" : "Modifica le impostazioni del prodotto selezionato" })
|
|
929
1274
|
] }),
|
|
930
|
-
/* @__PURE__ */ (0,
|
|
931
|
-
/* @__PURE__ */ (0,
|
|
932
|
-
/* @__PURE__ */ (0,
|
|
933
|
-
/* @__PURE__ */ (0,
|
|
934
|
-
formErrors.name && /* @__PURE__ */ (0,
|
|
1275
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "grid gap-4 py-4", children: [
|
|
1276
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "space-y-2", children: [
|
|
1277
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { className: "text-sm font-medium leading-none", children: "Nome *" }),
|
|
1278
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.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" }),
|
|
1279
|
+
formErrors.name && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-xs text-destructive", children: formErrors.name })
|
|
935
1280
|
] }),
|
|
936
|
-
/* @__PURE__ */ (0,
|
|
937
|
-
/* @__PURE__ */ (0,
|
|
938
|
-
/* @__PURE__ */ (0,
|
|
1281
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "space-y-2", children: [
|
|
1282
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { className: "text-sm font-medium leading-none", children: "Descrizione" }),
|
|
1283
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.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
1284
|
] }),
|
|
940
|
-
/* @__PURE__ */ (0,
|
|
941
|
-
/* @__PURE__ */ (0,
|
|
942
|
-
/* @__PURE__ */ (0,
|
|
943
|
-
/* @__PURE__ */ (0,
|
|
944
|
-
formErrors.price_cents && /* @__PURE__ */ (0,
|
|
1285
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
1286
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "space-y-2", children: [
|
|
1287
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { className: "text-sm font-medium leading-none", children: "Prezzo" }),
|
|
1288
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.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" }),
|
|
1289
|
+
formErrors.price_cents && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-xs text-destructive", children: formErrors.price_cents })
|
|
945
1290
|
] }),
|
|
946
|
-
/* @__PURE__ */ (0,
|
|
947
|
-
/* @__PURE__ */ (0,
|
|
948
|
-
/* @__PURE__ */ (0,
|
|
949
|
-
/* @__PURE__ */ (0,
|
|
950
|
-
/* @__PURE__ */ (0,
|
|
951
|
-
/* @__PURE__ */ (0,
|
|
1291
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "space-y-2", children: [
|
|
1292
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { className: "text-sm font-medium leading-none", children: "Valuta" }),
|
|
1293
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.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: [
|
|
1294
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "eur", children: "EUR" }),
|
|
1295
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "usd", children: "USD" }),
|
|
1296
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "gbp", children: "GBP" })
|
|
952
1297
|
] })
|
|
953
1298
|
] })
|
|
954
1299
|
] }),
|
|
955
|
-
/* @__PURE__ */ (0,
|
|
956
|
-
/* @__PURE__ */ (0,
|
|
957
|
-
/* @__PURE__ */ (0,
|
|
958
|
-
/* @__PURE__ */ (0,
|
|
959
|
-
/* @__PURE__ */ (0,
|
|
960
|
-
/* @__PURE__ */ (0,
|
|
961
|
-
/* @__PURE__ */ (0,
|
|
1300
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
1301
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "space-y-2", children: [
|
|
1302
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { className: "text-sm font-medium leading-none", children: "Ricorrenza" }),
|
|
1303
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.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: [
|
|
1304
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "one_time", children: "Una tantum" }),
|
|
1305
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "month", children: "Mensile" }),
|
|
1306
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "year", children: "Annuale" })
|
|
962
1307
|
] })
|
|
963
1308
|
] }),
|
|
964
|
-
/* @__PURE__ */ (0,
|
|
965
|
-
/* @__PURE__ */ (0,
|
|
966
|
-
/* @__PURE__ */ (0,
|
|
967
|
-
/* @__PURE__ */ (0,
|
|
968
|
-
/* @__PURE__ */ (0,
|
|
969
|
-
/* @__PURE__ */ (0,
|
|
970
|
-
/* @__PURE__ */ (0,
|
|
1309
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "space-y-2", children: [
|
|
1310
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { className: "text-sm font-medium leading-none", children: "Categoria" }),
|
|
1311
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.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: [
|
|
1312
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "feature", children: "Feature" }),
|
|
1313
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "addon", children: "Add-on" }),
|
|
1314
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "service", children: "Servizio" }),
|
|
1315
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "credits", children: "Crediti" })
|
|
971
1316
|
] })
|
|
972
1317
|
] })
|
|
973
1318
|
] }),
|
|
974
|
-
/* @__PURE__ */ (0,
|
|
975
|
-
/* @__PURE__ */ (0,
|
|
976
|
-
mode === "create" && /* @__PURE__ */ (0,
|
|
1319
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "space-y-4", children: [
|
|
1320
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ToggleRow, { label: "Prodotto Attivo", checked: formData.is_active, onChange: (v) => setFormData({ ...formData, is_active: v }) }),
|
|
1321
|
+
mode === "create" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ToggleRow, { label: "Sincronizza su Stripe", checked: formData.sync_stripe, onChange: (v) => setFormData({ ...formData, sync_stripe: v }) })
|
|
977
1322
|
] }),
|
|
978
|
-
features.length > 0 && /* @__PURE__ */ (0,
|
|
979
|
-
/* @__PURE__ */ (0,
|
|
980
|
-
/* @__PURE__ */ (0,
|
|
1323
|
+
features.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "space-y-2", children: [
|
|
1324
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { className: "text-sm font-medium leading-none", children: "Collega a Feature" }),
|
|
1325
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
981
1326
|
"select",
|
|
982
1327
|
{
|
|
983
1328
|
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
1329
|
value: formData.feature_slug || "none",
|
|
985
1330
|
onChange: (e) => setFormData({ ...formData, feature_slug: e.target.value === "none" ? "" : e.target.value }),
|
|
986
1331
|
children: [
|
|
987
|
-
/* @__PURE__ */ (0,
|
|
988
|
-
features.map((f) => /* @__PURE__ */ (0,
|
|
1332
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("option", { value: "none", children: "Nessuna feature" }),
|
|
1333
|
+
features.map((f) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("option", { value: f.slug, children: [
|
|
989
1334
|
f.name,
|
|
990
1335
|
" (",
|
|
991
1336
|
f.slug,
|
|
@@ -994,24 +1339,24 @@ function ProductFormDialog({
|
|
|
994
1339
|
]
|
|
995
1340
|
}
|
|
996
1341
|
),
|
|
997
|
-
formData.feature_slug && /* @__PURE__ */ (0,
|
|
998
|
-
/* @__PURE__ */ (0,
|
|
999
|
-
/* @__PURE__ */ (0,
|
|
1342
|
+
formData.feature_slug && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2 p-2 bg-blue-50 border border-blue-200 rounded text-sm", children: [
|
|
1343
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react6.Link, { className: "w-4 h-4 text-blue-600" }),
|
|
1344
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-blue-900", children: [
|
|
1000
1345
|
"Collegato a: ",
|
|
1001
|
-
/* @__PURE__ */ (0,
|
|
1346
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("strong", { children: formData.feature_slug })
|
|
1002
1347
|
] })
|
|
1003
1348
|
] })
|
|
1004
1349
|
] }),
|
|
1005
|
-
/* @__PURE__ */ (0,
|
|
1006
|
-
/* @__PURE__ */ (0,
|
|
1007
|
-
/* @__PURE__ */ (0,
|
|
1008
|
-
formErrors.extra_data && /* @__PURE__ */ (0,
|
|
1350
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "space-y-2", children: [
|
|
1351
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { className: "text-sm font-medium leading-none", children: "Extra Data (JSON)" }),
|
|
1352
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.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 }),
|
|
1353
|
+
formErrors.extra_data && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-xs text-destructive", children: formErrors.extra_data })
|
|
1009
1354
|
] })
|
|
1010
1355
|
] }),
|
|
1011
|
-
/* @__PURE__ */ (0,
|
|
1012
|
-
/* @__PURE__ */ (0,
|
|
1013
|
-
/* @__PURE__ */ (0,
|
|
1014
|
-
isSubmitting && /* @__PURE__ */ (0,
|
|
1356
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-4", children: [
|
|
1357
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.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" }),
|
|
1358
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.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: [
|
|
1359
|
+
isSubmitting && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react6.Loader2, { className: "w-4 h-4 animate-spin mr-2" }),
|
|
1015
1360
|
mode === "create" ? "Crea Prodotto" : "Salva Modifiche"
|
|
1016
1361
|
] })
|
|
1017
1362
|
] })
|
|
@@ -1019,8 +1364,8 @@ function ProductFormDialog({
|
|
|
1019
1364
|
] });
|
|
1020
1365
|
}
|
|
1021
1366
|
function ToggleRow({ label, checked, onChange }) {
|
|
1022
|
-
return /* @__PURE__ */ (0,
|
|
1023
|
-
/* @__PURE__ */ (0,
|
|
1367
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1368
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1024
1369
|
"button",
|
|
1025
1370
|
{
|
|
1026
1371
|
type: "button",
|
|
@@ -1028,15 +1373,15 @@ function ToggleRow({ label, checked, onChange }) {
|
|
|
1028
1373
|
"aria-checked": checked,
|
|
1029
1374
|
onClick: () => onChange(!checked),
|
|
1030
1375
|
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,
|
|
1376
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.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
1377
|
}
|
|
1033
1378
|
),
|
|
1034
|
-
/* @__PURE__ */ (0,
|
|
1379
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-sm font-medium leading-none", children: label })
|
|
1035
1380
|
] });
|
|
1036
1381
|
}
|
|
1037
1382
|
|
|
1038
1383
|
// src/pages/ProductsPage.tsx
|
|
1039
|
-
var
|
|
1384
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1040
1385
|
function ProductsPage({ wrapper: Wrapper, header }) {
|
|
1041
1386
|
const config = useBillingAdminConfig();
|
|
1042
1387
|
const featuresConfig = config.products?.features;
|
|
@@ -1108,81 +1453,81 @@ function ProductsPage({ wrapper: Wrapper, header }) {
|
|
|
1108
1453
|
});
|
|
1109
1454
|
}
|
|
1110
1455
|
};
|
|
1111
|
-
const content = /* @__PURE__ */ (0,
|
|
1456
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
1112
1457
|
header,
|
|
1113
|
-
/* @__PURE__ */ (0,
|
|
1114
|
-
/* @__PURE__ */ (0,
|
|
1115
|
-
/* @__PURE__ */ (0,
|
|
1116
|
-
/* @__PURE__ */ (0,
|
|
1117
|
-
/* @__PURE__ */ (0,
|
|
1458
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-4", children: [
|
|
1459
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1460
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
1461
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.Package, { className: "w-4 h-4" }),
|
|
1462
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { children: [
|
|
1118
1463
|
products.length,
|
|
1119
1464
|
" prodotti configurati"
|
|
1120
1465
|
] })
|
|
1121
1466
|
] }),
|
|
1122
|
-
/* @__PURE__ */ (0,
|
|
1123
|
-
/* @__PURE__ */ (0,
|
|
1124
|
-
/* @__PURE__ */ (0,
|
|
1467
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex gap-2", children: [
|
|
1468
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.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: [
|
|
1469
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.RefreshCw, { className: "w-4 h-4 mr-2" }),
|
|
1125
1470
|
"Aggiorna"
|
|
1126
1471
|
] }),
|
|
1127
|
-
/* @__PURE__ */ (0,
|
|
1128
|
-
/* @__PURE__ */ (0,
|
|
1472
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.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: [
|
|
1473
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.Plus, { className: "w-4 h-4 mr-2" }),
|
|
1129
1474
|
"Nuovo Prodotto"
|
|
1130
1475
|
] })
|
|
1131
1476
|
] })
|
|
1132
1477
|
] }),
|
|
1133
|
-
/* @__PURE__ */ (0,
|
|
1134
|
-
/* @__PURE__ */ (0,
|
|
1135
|
-
/* @__PURE__ */ (0,
|
|
1136
|
-
/* @__PURE__ */ (0,
|
|
1137
|
-
/* @__PURE__ */ (0,
|
|
1138
|
-
/* @__PURE__ */ (0,
|
|
1139
|
-
/* @__PURE__ */ (0,
|
|
1140
|
-
/* @__PURE__ */ (0,
|
|
1141
|
-
/* @__PURE__ */ (0,
|
|
1478
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "rounded-lg border bg-card shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("table", { className: "w-full caption-bottom text-sm", children: [
|
|
1479
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("thead", { className: "[&_tr]:border-b", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
|
|
1480
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Nome" }),
|
|
1481
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Categoria" }),
|
|
1482
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Prezzo" }),
|
|
1483
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Ricorrenza" }),
|
|
1484
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Attivo" }),
|
|
1485
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Stripe" }),
|
|
1486
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { className: "h-12 px-4 text-right font-medium text-muted-foreground", children: "Azioni" })
|
|
1142
1487
|
] }) }),
|
|
1143
|
-
/* @__PURE__ */ (0,
|
|
1488
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("tbody", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SkeletonRows, { rows: 3, columns: 7 }) : products.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { colSpan: 7, className: "p-4 text-center py-8 text-muted-foreground", children: "Nessun prodotto configurato" }) }) : products.map((product) => {
|
|
1144
1489
|
const hasFeature = !!product.extra_data?.feature_slug;
|
|
1145
|
-
return /* @__PURE__ */ (0,
|
|
1146
|
-
/* @__PURE__ */ (0,
|
|
1147
|
-
/* @__PURE__ */ (0,
|
|
1148
|
-
/* @__PURE__ */ (0,
|
|
1149
|
-
hasFeature && /* @__PURE__ */ (0,
|
|
1490
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
|
|
1491
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
|
|
1492
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1493
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "font-medium", children: product.name }),
|
|
1494
|
+
hasFeature && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(StatusBadge, { label: "Feature", colorClass: "bg-blue-50 text-blue-700 border-blue-200" })
|
|
1150
1495
|
] }),
|
|
1151
|
-
product.description && /* @__PURE__ */ (0,
|
|
1496
|
+
product.description && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-sm text-muted-foreground truncate max-w-xs", children: product.description })
|
|
1152
1497
|
] }) }),
|
|
1153
|
-
/* @__PURE__ */ (0,
|
|
1154
|
-
/* @__PURE__ */ (0,
|
|
1155
|
-
/* @__PURE__ */ (0,
|
|
1156
|
-
/* @__PURE__ */ (0,
|
|
1157
|
-
/* @__PURE__ */ (0,
|
|
1158
|
-
/* @__PURE__ */ (0,
|
|
1159
|
-
/* @__PURE__ */ (0,
|
|
1160
|
-
/* @__PURE__ */ (0,
|
|
1498
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(StatusBadge, { label: product.category || "feature", colorClass: "border" }) }),
|
|
1499
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "p-4", children: (0, import_growflowbilling_admin_core4.formatCurrencyFromCents)(product.price_cents, product.currency) }),
|
|
1500
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "p-4", children: product.recurring_interval ? /* @__PURE__ */ (0, import_jsx_runtime17.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_runtime17.jsx)("span", { className: "text-muted-foreground", children: "Una tantum" }) }),
|
|
1501
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "p-4", children: product.is_active ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.Check, { className: "w-4 h-4 text-green-600" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.X, { className: "w-4 h-4 text-muted-foreground" }) }),
|
|
1502
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "p-4", children: product.stripe_product_id ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(StatusBadge, { label: "Synced", colorClass: "bg-green-50 text-green-700 border-green-200" }) : /* @__PURE__ */ (0, import_jsx_runtime17.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_runtime17.jsx)(import_lucide_react7.Upload, { className: "w-4 h-4" }) }) }),
|
|
1503
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "p-4 text-right", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex justify-end gap-1", children: [
|
|
1504
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.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_runtime17.jsx)(import_lucide_react7.Pencil, { className: "w-4 h-4" }) }),
|
|
1505
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.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_runtime17.jsx)(import_lucide_react7.Trash2, { className: "w-4 h-4" }) })
|
|
1161
1506
|
] }) })
|
|
1162
1507
|
] }, product.id);
|
|
1163
1508
|
}) })
|
|
1164
1509
|
] }) }) })
|
|
1165
1510
|
] }),
|
|
1166
|
-
/* @__PURE__ */ (0,
|
|
1167
|
-
/* @__PURE__ */ (0,
|
|
1168
|
-
/* @__PURE__ */ (0,
|
|
1511
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ProductFormDialog, { isOpen: createDialog.isOpen, onOpenChange: (open) => open ? createDialog.open() : createDialog.close(), mode: "create", features, onSubmit: handleCreateSubmit, isSubmitting: createMutation.isPending }),
|
|
1512
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.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 }),
|
|
1513
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DeleteConfirmDialog, { isOpen: deleteDialog.isOpen, onOpenChange: (open) => !open && deleteDialog.close(), title: "Disattivare Prodotto?", description: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
1169
1514
|
"Stai per disattivare il prodotto ",
|
|
1170
|
-
/* @__PURE__ */ (0,
|
|
1515
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: deleteDialog.data?.name }),
|
|
1171
1516
|
". Il prodotto non sar\xE0 pi\xF9 acquistabile."
|
|
1172
1517
|
] }), onConfirm: handleDeleteConfirm, isDeleting: deleteMutation.isPending, confirmLabel: "Disattiva Prodotto" })
|
|
1173
1518
|
] });
|
|
1174
|
-
return Wrapper ? /* @__PURE__ */ (0,
|
|
1519
|
+
return Wrapper ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Wrapper, { children: content }) : content;
|
|
1175
1520
|
}
|
|
1176
1521
|
|
|
1177
1522
|
// src/pages/SubscriptionsPage.tsx
|
|
1178
|
-
var
|
|
1523
|
+
var import_react7 = require("react");
|
|
1179
1524
|
var import_sonner3 = require("sonner");
|
|
1180
|
-
var
|
|
1525
|
+
var import_lucide_react9 = require("lucide-react");
|
|
1181
1526
|
var import_growflowbilling_admin_core5 = require("@growflowstudio/growflowbilling-admin-core");
|
|
1182
1527
|
|
|
1183
1528
|
// src/components/shared/FilterBar.tsx
|
|
1184
|
-
var
|
|
1185
|
-
var
|
|
1529
|
+
var import_lucide_react8 = require("lucide-react");
|
|
1530
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1186
1531
|
function FilterBar({
|
|
1187
1532
|
searchPlaceholder = "Cerca...",
|
|
1188
1533
|
searchValue,
|
|
@@ -1192,10 +1537,10 @@ function FilterBar({
|
|
|
1192
1537
|
statusOptions,
|
|
1193
1538
|
onRefresh
|
|
1194
1539
|
}) {
|
|
1195
|
-
return /* @__PURE__ */ (0,
|
|
1196
|
-
/* @__PURE__ */ (0,
|
|
1197
|
-
/* @__PURE__ */ (0,
|
|
1198
|
-
/* @__PURE__ */ (0,
|
|
1540
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "rounded-lg border bg-card text-card-foreground shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "p-6 pt-6", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col sm:flex-row gap-4", children: [
|
|
1541
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "relative flex-1", children: [
|
|
1542
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" }),
|
|
1543
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1199
1544
|
"input",
|
|
1200
1545
|
{
|
|
1201
1546
|
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",
|
|
@@ -1205,22 +1550,22 @@ function FilterBar({
|
|
|
1205
1550
|
}
|
|
1206
1551
|
)
|
|
1207
1552
|
] }),
|
|
1208
|
-
statusOptions && onStatusFilterChange && /* @__PURE__ */ (0,
|
|
1553
|
+
statusOptions && onStatusFilterChange && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1209
1554
|
"select",
|
|
1210
1555
|
{
|
|
1211
1556
|
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
1557
|
value: statusFilter || "all",
|
|
1213
1558
|
onChange: (e) => onStatusFilterChange(e.target.value),
|
|
1214
|
-
children: statusOptions.map((opt) => /* @__PURE__ */ (0,
|
|
1559
|
+
children: statusOptions.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
|
|
1215
1560
|
}
|
|
1216
1561
|
),
|
|
1217
|
-
/* @__PURE__ */ (0,
|
|
1562
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
1218
1563
|
"button",
|
|
1219
1564
|
{
|
|
1220
1565
|
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
1566
|
onClick: onRefresh,
|
|
1222
1567
|
children: [
|
|
1223
|
-
/* @__PURE__ */ (0,
|
|
1568
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.RefreshCw, { className: "w-4 h-4 mr-2" }),
|
|
1224
1569
|
"Aggiorna"
|
|
1225
1570
|
]
|
|
1226
1571
|
}
|
|
@@ -1229,7 +1574,7 @@ function FilterBar({
|
|
|
1229
1574
|
}
|
|
1230
1575
|
|
|
1231
1576
|
// src/pages/SubscriptionsPage.tsx
|
|
1232
|
-
var
|
|
1577
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1233
1578
|
var statusFilterOptions = [
|
|
1234
1579
|
{ value: "all", label: "Tutti gli stati" },
|
|
1235
1580
|
{ value: "active", label: "Attivi" },
|
|
@@ -1239,12 +1584,12 @@ var statusFilterOptions = [
|
|
|
1239
1584
|
{ value: "unpaid", label: "Non Pagati" }
|
|
1240
1585
|
];
|
|
1241
1586
|
function SubscriptionsPage({ wrapper: Wrapper, header }) {
|
|
1242
|
-
const [search, setSearch] = (0,
|
|
1243
|
-
const [statusFilter, setStatusFilter] = (0,
|
|
1244
|
-
const [selectedSub, setSelectedSub] = (0,
|
|
1245
|
-
const [showDetails, setShowDetails] = (0,
|
|
1246
|
-
const [showCancelDialog, setShowCancelDialog] = (0,
|
|
1247
|
-
const [cancelAtPeriodEnd, setCancelAtPeriodEnd] = (0,
|
|
1587
|
+
const [search, setSearch] = (0, import_react7.useState)("");
|
|
1588
|
+
const [statusFilter, setStatusFilter] = (0, import_react7.useState)("all");
|
|
1589
|
+
const [selectedSub, setSelectedSub] = (0, import_react7.useState)(null);
|
|
1590
|
+
const [showDetails, setShowDetails] = (0, import_react7.useState)(false);
|
|
1591
|
+
const [showCancelDialog, setShowCancelDialog] = (0, import_react7.useState)(false);
|
|
1592
|
+
const [cancelAtPeriodEnd, setCancelAtPeriodEnd] = (0, import_react7.useState)(true);
|
|
1248
1593
|
const { data, isLoading, refetch } = (0, import_growflowbilling_admin_core5.useAdminSubscriptions)({
|
|
1249
1594
|
search: search || void 0,
|
|
1250
1595
|
status: statusFilter !== "all" ? statusFilter : void 0,
|
|
@@ -1265,10 +1610,10 @@ function SubscriptionsPage({ wrapper: Wrapper, header }) {
|
|
|
1265
1610
|
});
|
|
1266
1611
|
}
|
|
1267
1612
|
};
|
|
1268
|
-
const content = /* @__PURE__ */ (0,
|
|
1613
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
|
|
1269
1614
|
header,
|
|
1270
|
-
/* @__PURE__ */ (0,
|
|
1271
|
-
/* @__PURE__ */ (0,
|
|
1615
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "space-y-4", children: [
|
|
1616
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1272
1617
|
FilterBar,
|
|
1273
1618
|
{
|
|
1274
1619
|
searchPlaceholder: "Cerca per email o ID esterno...",
|
|
@@ -1280,167 +1625,167 @@ function SubscriptionsPage({ wrapper: Wrapper, header }) {
|
|
|
1280
1625
|
onRefresh: refetch
|
|
1281
1626
|
}
|
|
1282
1627
|
),
|
|
1283
|
-
/* @__PURE__ */ (0,
|
|
1284
|
-
/* @__PURE__ */ (0,
|
|
1285
|
-
/* @__PURE__ */ (0,
|
|
1628
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
1629
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.CreditCard, { className: "w-4 h-4" }),
|
|
1630
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { children: [
|
|
1286
1631
|
total,
|
|
1287
1632
|
" abbonament",
|
|
1288
1633
|
total === 1 ? "o" : "i",
|
|
1289
1634
|
" trovati"
|
|
1290
1635
|
] })
|
|
1291
1636
|
] }),
|
|
1292
|
-
/* @__PURE__ */ (0,
|
|
1293
|
-
/* @__PURE__ */ (0,
|
|
1294
|
-
/* @__PURE__ */ (0,
|
|
1295
|
-
/* @__PURE__ */ (0,
|
|
1296
|
-
/* @__PURE__ */ (0,
|
|
1297
|
-
/* @__PURE__ */ (0,
|
|
1298
|
-
/* @__PURE__ */ (0,
|
|
1299
|
-
/* @__PURE__ */ (0,
|
|
1300
|
-
/* @__PURE__ */ (0,
|
|
1301
|
-
/* @__PURE__ */ (0,
|
|
1637
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "rounded-lg border bg-card shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("table", { className: "w-full caption-bottom text-sm", children: [
|
|
1638
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("thead", { className: "[&_tr]:border-b", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
|
|
1639
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Subscriber" }),
|
|
1640
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Piano" }),
|
|
1641
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Stato" }),
|
|
1642
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Ciclo" }),
|
|
1643
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Periodo" }),
|
|
1644
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Trial End" }),
|
|
1645
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Creato" }),
|
|
1646
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("th", { className: "h-12 px-4 text-right font-medium text-muted-foreground", children: "Azioni" })
|
|
1302
1647
|
] }) }),
|
|
1303
|
-
/* @__PURE__ */ (0,
|
|
1304
|
-
/* @__PURE__ */ (0,
|
|
1305
|
-
/* @__PURE__ */ (0,
|
|
1306
|
-
/* @__PURE__ */ (0,
|
|
1648
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("tbody", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SkeletonRows, { rows: 5, columns: 8 }) : subscriptions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime19.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_runtime19.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
|
|
1649
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col", children: [
|
|
1650
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "font-medium", children: sub.subscriber_email }),
|
|
1651
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-xs text-muted-foreground", children: sub.subscriber_external_id })
|
|
1307
1652
|
] }) }),
|
|
1308
|
-
/* @__PURE__ */ (0,
|
|
1309
|
-
/* @__PURE__ */ (0,
|
|
1310
|
-
/* @__PURE__ */ (0,
|
|
1653
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("td", { className: "p-4", children: [
|
|
1654
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "font-medium", children: sub.plan_name }),
|
|
1655
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "text-xs text-muted-foreground ml-1", children: [
|
|
1311
1656
|
"(",
|
|
1312
1657
|
sub.plan_slug,
|
|
1313
1658
|
")"
|
|
1314
1659
|
] })
|
|
1315
1660
|
] }),
|
|
1316
|
-
/* @__PURE__ */ (0,
|
|
1317
|
-
/* @__PURE__ */ (0,
|
|
1318
|
-
/* @__PURE__ */ (0,
|
|
1661
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StatusBadge, { label: import_growflowbilling_admin_core5.subscriptionStatusLabels[sub.status], colorClass: import_growflowbilling_admin_core5.subscriptionStatusColors[sub.status] }) }),
|
|
1662
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("td", { className: "p-4 capitalize", children: sub.billing_cycle }),
|
|
1663
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("td", { className: "p-4 text-sm", children: [
|
|
1319
1664
|
(0, import_growflowbilling_admin_core5.formatDate)(sub.current_period_start),
|
|
1320
1665
|
" - ",
|
|
1321
1666
|
(0, import_growflowbilling_admin_core5.formatDate)(sub.current_period_end)
|
|
1322
1667
|
] }),
|
|
1323
|
-
/* @__PURE__ */ (0,
|
|
1324
|
-
/* @__PURE__ */ (0,
|
|
1325
|
-
/* @__PURE__ */ (0,
|
|
1326
|
-
/* @__PURE__ */ (0,
|
|
1668
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("td", { className: "p-4", children: (0, import_growflowbilling_admin_core5.formatDate)(sub.trial_end) }),
|
|
1669
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("td", { className: "p-4", children: (0, import_growflowbilling_admin_core5.formatDate)(sub.created_at) }),
|
|
1670
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("td", { className: "p-4 text-right", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex justify-end gap-1", children: [
|
|
1671
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { className: "inline-flex items-center justify-center rounded-md h-9 w-9 hover:bg-accent", onClick: () => {
|
|
1327
1672
|
setSelectedSub(sub);
|
|
1328
1673
|
setShowDetails(true);
|
|
1329
|
-
}, children: /* @__PURE__ */ (0,
|
|
1330
|
-
sub.status !== "canceled" && /* @__PURE__ */ (0,
|
|
1674
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.Eye, { className: "w-4 h-4" }) }),
|
|
1675
|
+
sub.status !== "canceled" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { className: "inline-flex items-center justify-center rounded-md h-9 w-9 text-destructive hover:bg-accent", onClick: () => {
|
|
1331
1676
|
setSelectedSub(sub);
|
|
1332
1677
|
setShowCancelDialog(true);
|
|
1333
|
-
}, children: /* @__PURE__ */ (0,
|
|
1678
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.XCircle, { className: "w-4 h-4" }) })
|
|
1334
1679
|
] }) })
|
|
1335
1680
|
] }, sub.id)) })
|
|
1336
1681
|
] }) }) })
|
|
1337
1682
|
] }),
|
|
1338
|
-
showDetails && selectedSub && /* @__PURE__ */ (0,
|
|
1339
|
-
/* @__PURE__ */ (0,
|
|
1340
|
-
/* @__PURE__ */ (0,
|
|
1341
|
-
/* @__PURE__ */ (0,
|
|
1342
|
-
/* @__PURE__ */ (0,
|
|
1343
|
-
/* @__PURE__ */ (0,
|
|
1344
|
-
/* @__PURE__ */ (0,
|
|
1345
|
-
/* @__PURE__ */ (0,
|
|
1346
|
-
/* @__PURE__ */ (0,
|
|
1347
|
-
/* @__PURE__ */ (0,
|
|
1348
|
-
/* @__PURE__ */ (0,
|
|
1349
|
-
/* @__PURE__ */ (0,
|
|
1683
|
+
showDetails && selectedSub && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
1684
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => setShowDetails(false) }),
|
|
1685
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "relative z-50 w-full max-w-lg rounded-lg border bg-background p-6 shadow-lg", children: [
|
|
1686
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { className: "text-lg font-semibold mb-1", children: "Dettagli Abbonamento" }),
|
|
1687
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground mb-4", children: "Informazioni complete" }),
|
|
1688
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "space-y-4", children: [
|
|
1689
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
1690
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
|
|
1691
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground", children: "Subscriber" }),
|
|
1692
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2 mt-1", children: [
|
|
1693
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.User, { className: "w-4 h-4 text-muted-foreground" }),
|
|
1694
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "font-medium", children: selectedSub.subscriber_email })
|
|
1350
1695
|
] }),
|
|
1351
|
-
/* @__PURE__ */ (0,
|
|
1696
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "text-xs text-muted-foreground mt-1", children: [
|
|
1352
1697
|
"ID: ",
|
|
1353
1698
|
selectedSub.subscriber_external_id
|
|
1354
1699
|
] })
|
|
1355
1700
|
] }),
|
|
1356
|
-
/* @__PURE__ */ (0,
|
|
1357
|
-
/* @__PURE__ */ (0,
|
|
1358
|
-
/* @__PURE__ */ (0,
|
|
1359
|
-
/* @__PURE__ */ (0,
|
|
1360
|
-
/* @__PURE__ */ (0,
|
|
1701
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
|
|
1702
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground", children: "Piano" }),
|
|
1703
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2 mt-1", children: [
|
|
1704
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.CreditCard, { className: "w-4 h-4 text-muted-foreground" }),
|
|
1705
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "font-medium", children: selectedSub.plan_name })
|
|
1361
1706
|
] })
|
|
1362
1707
|
] })
|
|
1363
1708
|
] }),
|
|
1364
|
-
/* @__PURE__ */ (0,
|
|
1365
|
-
/* @__PURE__ */ (0,
|
|
1366
|
-
/* @__PURE__ */ (0,
|
|
1367
|
-
/* @__PURE__ */ (0,
|
|
1709
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
1710
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
|
|
1711
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground", children: "Stato" }),
|
|
1712
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StatusBadge, { label: import_growflowbilling_admin_core5.subscriptionStatusLabels[selectedSub.status], colorClass: import_growflowbilling_admin_core5.subscriptionStatusColors[selectedSub.status], className: "mt-1" })
|
|
1368
1713
|
] }),
|
|
1369
|
-
/* @__PURE__ */ (0,
|
|
1370
|
-
/* @__PURE__ */ (0,
|
|
1371
|
-
/* @__PURE__ */ (0,
|
|
1714
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
|
|
1715
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground", children: "Ciclo" }),
|
|
1716
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "font-medium capitalize mt-1", children: selectedSub.billing_cycle })
|
|
1372
1717
|
] })
|
|
1373
1718
|
] }),
|
|
1374
|
-
/* @__PURE__ */ (0,
|
|
1375
|
-
/* @__PURE__ */ (0,
|
|
1376
|
-
/* @__PURE__ */ (0,
|
|
1377
|
-
/* @__PURE__ */ (0,
|
|
1378
|
-
/* @__PURE__ */ (0,
|
|
1379
|
-
/* @__PURE__ */ (0,
|
|
1719
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
1720
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
|
|
1721
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground", children: "Periodo" }),
|
|
1722
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2 mt-1", children: [
|
|
1723
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.Calendar, { className: "w-4 h-4 text-muted-foreground" }),
|
|
1724
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { children: [
|
|
1380
1725
|
(0, import_growflowbilling_admin_core5.formatDate)(selectedSub.current_period_start),
|
|
1381
1726
|
" - ",
|
|
1382
1727
|
(0, import_growflowbilling_admin_core5.formatDate)(selectedSub.current_period_end)
|
|
1383
1728
|
] })
|
|
1384
1729
|
] })
|
|
1385
1730
|
] }),
|
|
1386
|
-
/* @__PURE__ */ (0,
|
|
1387
|
-
/* @__PURE__ */ (0,
|
|
1388
|
-
/* @__PURE__ */ (0,
|
|
1731
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
|
|
1732
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground", children: "Fine Trial" }),
|
|
1733
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "mt-1", children: (0, import_growflowbilling_admin_core5.formatDate)(selectedSub.trial_end) })
|
|
1389
1734
|
] })
|
|
1390
1735
|
] }),
|
|
1391
|
-
selectedSub.canceled_at && /* @__PURE__ */ (0,
|
|
1392
|
-
/* @__PURE__ */ (0,
|
|
1736
|
+
selectedSub.canceled_at && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "p-3 bg-destructive/10 rounded-lg", children: [
|
|
1737
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "text-sm text-destructive font-medium", children: [
|
|
1393
1738
|
"Cancellato il ",
|
|
1394
1739
|
(0, import_growflowbilling_admin_core5.formatDate)(selectedSub.canceled_at)
|
|
1395
1740
|
] }),
|
|
1396
|
-
selectedSub.cancel_at && /* @__PURE__ */ (0,
|
|
1741
|
+
selectedSub.cancel_at && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "text-xs text-destructive/80 mt-1", children: [
|
|
1397
1742
|
"Termina il ",
|
|
1398
1743
|
(0, import_growflowbilling_admin_core5.formatDate)(selectedSub.cancel_at)
|
|
1399
1744
|
] })
|
|
1400
1745
|
] }),
|
|
1401
|
-
selectedSub.stripe_subscription_id && /* @__PURE__ */ (0,
|
|
1402
|
-
/* @__PURE__ */ (0,
|
|
1403
|
-
/* @__PURE__ */ (0,
|
|
1746
|
+
selectedSub.stripe_subscription_id && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
|
|
1747
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground", children: "Stripe ID" }),
|
|
1748
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("code", { className: "text-xs bg-muted px-2 py-1 rounded mt-1 inline-block", children: selectedSub.stripe_subscription_id })
|
|
1404
1749
|
] })
|
|
1405
1750
|
] }),
|
|
1406
|
-
/* @__PURE__ */ (0,
|
|
1751
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex justify-end pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime19.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
1752
|
] })
|
|
1408
1753
|
] }),
|
|
1409
|
-
showCancelDialog && selectedSub && /* @__PURE__ */ (0,
|
|
1410
|
-
/* @__PURE__ */ (0,
|
|
1411
|
-
/* @__PURE__ */ (0,
|
|
1412
|
-
/* @__PURE__ */ (0,
|
|
1413
|
-
/* @__PURE__ */ (0,
|
|
1754
|
+
showCancelDialog && selectedSub && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
1755
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => setShowCancelDialog(false) }),
|
|
1756
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "relative z-50 w-full max-w-lg rounded-lg border bg-background p-6 shadow-lg", children: [
|
|
1757
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { className: "text-lg font-semibold", children: "Cancellare Abbonamento?" }),
|
|
1758
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "text-sm text-muted-foreground mt-1", children: [
|
|
1414
1759
|
"Stai per cancellare l'abbonamento di ",
|
|
1415
|
-
/* @__PURE__ */ (0,
|
|
1760
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("strong", { children: selectedSub.subscriber_email }),
|
|
1416
1761
|
"."
|
|
1417
1762
|
] }),
|
|
1418
|
-
/* @__PURE__ */ (0,
|
|
1419
|
-
/* @__PURE__ */ (0,
|
|
1420
|
-
/* @__PURE__ */ (0,
|
|
1421
|
-
/* @__PURE__ */ (0,
|
|
1763
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "py-4", children: [
|
|
1764
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer", children: [
|
|
1765
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("input", { type: "checkbox", checked: cancelAtPeriodEnd, onChange: (e) => setCancelAtPeriodEnd(e.target.checked), className: "rounded border-input" }),
|
|
1766
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-sm", children: "Cancella alla fine del periodo corrente (consigliato)" })
|
|
1422
1767
|
] }),
|
|
1423
|
-
/* @__PURE__ */ (0,
|
|
1768
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.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
1769
|
] }),
|
|
1425
|
-
/* @__PURE__ */ (0,
|
|
1426
|
-
/* @__PURE__ */ (0,
|
|
1427
|
-
/* @__PURE__ */ (0,
|
|
1770
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex justify-end gap-2", children: [
|
|
1771
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.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" }),
|
|
1772
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.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
1773
|
] })
|
|
1429
1774
|
] })
|
|
1430
1775
|
] })
|
|
1431
1776
|
] });
|
|
1432
|
-
return Wrapper ? /* @__PURE__ */ (0,
|
|
1777
|
+
return Wrapper ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Wrapper, { children: content }) : content;
|
|
1433
1778
|
}
|
|
1434
1779
|
|
|
1435
1780
|
// src/pages/PaymentsPage.tsx
|
|
1436
|
-
var
|
|
1781
|
+
var import_react8 = require("react");
|
|
1437
1782
|
var import_sonner4 = require("sonner");
|
|
1438
|
-
var
|
|
1783
|
+
var import_lucide_react11 = require("lucide-react");
|
|
1439
1784
|
var import_growflowbilling_admin_core6 = require("@growflowstudio/growflowbilling-admin-core");
|
|
1440
1785
|
|
|
1441
1786
|
// src/components/shared/CursorPagination.tsx
|
|
1442
|
-
var
|
|
1443
|
-
var
|
|
1787
|
+
var import_lucide_react10 = require("lucide-react");
|
|
1788
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1444
1789
|
function CursorPagination({
|
|
1445
1790
|
cursor,
|
|
1446
1791
|
hasMore,
|
|
@@ -1448,20 +1793,20 @@ function CursorPagination({
|
|
|
1448
1793
|
onFirstPage,
|
|
1449
1794
|
onNextPage
|
|
1450
1795
|
}) {
|
|
1451
|
-
return /* @__PURE__ */ (0,
|
|
1452
|
-
/* @__PURE__ */ (0,
|
|
1796
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex justify-between items-center", children: [
|
|
1797
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
1453
1798
|
"button",
|
|
1454
1799
|
{
|
|
1455
1800
|
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
1801
|
onClick: onFirstPage,
|
|
1457
1802
|
disabled: !cursor,
|
|
1458
1803
|
children: [
|
|
1459
|
-
/* @__PURE__ */ (0,
|
|
1804
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react10.ChevronLeft, { className: "w-4 h-4 mr-1" }),
|
|
1460
1805
|
"Prima Pagina"
|
|
1461
1806
|
]
|
|
1462
1807
|
}
|
|
1463
1808
|
),
|
|
1464
|
-
/* @__PURE__ */ (0,
|
|
1809
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
1465
1810
|
"button",
|
|
1466
1811
|
{
|
|
1467
1812
|
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",
|
|
@@ -1473,7 +1818,7 @@ function CursorPagination({
|
|
|
1473
1818
|
disabled: !hasMore,
|
|
1474
1819
|
children: [
|
|
1475
1820
|
"Prossima",
|
|
1476
|
-
/* @__PURE__ */ (0,
|
|
1821
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react10.ChevronRight, { className: "w-4 h-4 ml-1" })
|
|
1477
1822
|
]
|
|
1478
1823
|
}
|
|
1479
1824
|
)
|
|
@@ -1481,7 +1826,7 @@ function CursorPagination({
|
|
|
1481
1826
|
}
|
|
1482
1827
|
|
|
1483
1828
|
// src/pages/PaymentsPage.tsx
|
|
1484
|
-
var
|
|
1829
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1485
1830
|
var statusFilterOptions2 = [
|
|
1486
1831
|
{ value: "all", label: "Tutti gli stati" },
|
|
1487
1832
|
{ value: "succeeded", label: "Completati" },
|
|
@@ -1490,13 +1835,13 @@ var statusFilterOptions2 = [
|
|
|
1490
1835
|
{ value: "canceled", label: "Annullati" }
|
|
1491
1836
|
];
|
|
1492
1837
|
function PaymentsPage({ wrapper: Wrapper, header }) {
|
|
1493
|
-
const [search, setSearch] = (0,
|
|
1494
|
-
const [statusFilter, setStatusFilter] = (0,
|
|
1495
|
-
const
|
|
1496
|
-
const [selectedPayment, setSelectedPayment] = (0,
|
|
1497
|
-
const [showRefundDialog, setShowRefundDialog] = (0,
|
|
1498
|
-
const [refundAmount, setRefundAmount] = (0,
|
|
1499
|
-
const [refundReason, setRefundReason] = (0,
|
|
1838
|
+
const [search, setSearch] = (0, import_react8.useState)("");
|
|
1839
|
+
const [statusFilter, setStatusFilter] = (0, import_react8.useState)("all");
|
|
1840
|
+
const { cursor, goToNext, goToFirst } = (0, import_growflowbilling_admin_core6.useCursorPagination)();
|
|
1841
|
+
const [selectedPayment, setSelectedPayment] = (0, import_react8.useState)(null);
|
|
1842
|
+
const [showRefundDialog, setShowRefundDialog] = (0, import_react8.useState)(false);
|
|
1843
|
+
const [refundAmount, setRefundAmount] = (0, import_react8.useState)("");
|
|
1844
|
+
const [refundReason, setRefundReason] = (0, import_react8.useState)("requested_by_customer");
|
|
1500
1845
|
const { data, isLoading, refetch } = (0, import_growflowbilling_admin_core6.useAdminPayments)({
|
|
1501
1846
|
search: search || void 0,
|
|
1502
1847
|
status: statusFilter !== "all" ? statusFilter : void 0,
|
|
@@ -1524,179 +1869,179 @@ function PaymentsPage({ wrapper: Wrapper, header }) {
|
|
|
1524
1869
|
setRefundAmount((payment.amount / 100).toFixed(2));
|
|
1525
1870
|
setShowRefundDialog(true);
|
|
1526
1871
|
};
|
|
1527
|
-
const content = /* @__PURE__ */ (0,
|
|
1872
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
1528
1873
|
header,
|
|
1529
|
-
/* @__PURE__ */ (0,
|
|
1530
|
-
/* @__PURE__ */ (0,
|
|
1874
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "space-y-4", children: [
|
|
1875
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1531
1876
|
FilterBar,
|
|
1532
1877
|
{
|
|
1533
1878
|
searchPlaceholder: "Cerca per email cliente...",
|
|
1534
1879
|
searchValue: search,
|
|
1535
1880
|
onSearchChange: (v) => {
|
|
1536
1881
|
setSearch(v);
|
|
1537
|
-
|
|
1882
|
+
goToFirst();
|
|
1538
1883
|
},
|
|
1539
1884
|
statusFilter,
|
|
1540
1885
|
onStatusFilterChange: (v) => {
|
|
1541
1886
|
setStatusFilter(v);
|
|
1542
|
-
|
|
1887
|
+
goToFirst();
|
|
1543
1888
|
},
|
|
1544
1889
|
statusOptions: statusFilterOptions2,
|
|
1545
1890
|
onRefresh: refetch
|
|
1546
1891
|
}
|
|
1547
1892
|
),
|
|
1548
|
-
/* @__PURE__ */ (0,
|
|
1549
|
-
/* @__PURE__ */ (0,
|
|
1550
|
-
/* @__PURE__ */ (0,
|
|
1893
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
1894
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react11.Receipt, { className: "w-4 h-4" }),
|
|
1895
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { children: [
|
|
1551
1896
|
payments.length,
|
|
1552
1897
|
" pagamenti visualizzati"
|
|
1553
1898
|
] })
|
|
1554
1899
|
] }),
|
|
1555
|
-
/* @__PURE__ */ (0,
|
|
1556
|
-
/* @__PURE__ */ (0,
|
|
1557
|
-
/* @__PURE__ */ (0,
|
|
1558
|
-
/* @__PURE__ */ (0,
|
|
1559
|
-
/* @__PURE__ */ (0,
|
|
1560
|
-
/* @__PURE__ */ (0,
|
|
1561
|
-
/* @__PURE__ */ (0,
|
|
1562
|
-
/* @__PURE__ */ (0,
|
|
1563
|
-
/* @__PURE__ */ (0,
|
|
1900
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "rounded-lg border bg-card shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("table", { className: "w-full caption-bottom text-sm", children: [
|
|
1901
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("thead", { className: "[&_tr]:border-b", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
|
|
1902
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "ID Pagamento" }),
|
|
1903
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Importo" }),
|
|
1904
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Stato" }),
|
|
1905
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Cliente" }),
|
|
1906
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Descrizione" }),
|
|
1907
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Data" }),
|
|
1908
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("th", { className: "h-12 px-4 text-right font-medium text-muted-foreground", children: "Azioni" })
|
|
1564
1909
|
] }) }),
|
|
1565
|
-
/* @__PURE__ */ (0,
|
|
1566
|
-
/* @__PURE__ */ (0,
|
|
1567
|
-
/* @__PURE__ */ (0,
|
|
1568
|
-
/* @__PURE__ */ (0,
|
|
1569
|
-
/* @__PURE__ */ (0,
|
|
1570
|
-
/* @__PURE__ */ (0,
|
|
1571
|
-
/* @__PURE__ */ (0,
|
|
1572
|
-
/* @__PURE__ */ (0,
|
|
1573
|
-
/* @__PURE__ */ (0,
|
|
1910
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("tbody", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SkeletonRows, { rows: 5, columns: 7 }) : payments.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
|
|
1911
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("code", { className: "text-xs bg-muted px-2 py-0.5 rounded", children: (0, import_growflowbilling_admin_core6.truncateId)(payment.id) }) }),
|
|
1912
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("td", { className: "p-4 font-medium", children: (0, import_growflowbilling_admin_core6.formatCurrencyFromCents)(payment.amount, payment.currency) }),
|
|
1913
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StatusBadge, { label: import_growflowbilling_admin_core6.paymentStatusLabels[payment.status], colorClass: import_growflowbilling_admin_core6.paymentStatusColors[payment.status] }) }),
|
|
1914
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("td", { className: "p-4", children: payment.customer_email || /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-muted-foreground", children: "-" }) }),
|
|
1915
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("td", { className: "p-4 max-w-[200px] truncate", children: payment.description || /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-muted-foreground", children: "-" }) }),
|
|
1916
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("td", { className: "p-4", children: (0, import_growflowbilling_admin_core6.formatTimestamp)(payment.created, true) }),
|
|
1917
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("td", { className: "p-4 text-right", children: payment.status === "succeeded" && /* @__PURE__ */ (0, import_jsx_runtime21.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: [
|
|
1918
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react11.RotateCcw, { className: "w-4 h-4 mr-1" }),
|
|
1574
1919
|
"Rimborsa"
|
|
1575
1920
|
] }) })
|
|
1576
1921
|
] }, payment.id)) })
|
|
1577
1922
|
] }) }) }),
|
|
1578
|
-
/* @__PURE__ */ (0,
|
|
1923
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CursorPagination, { cursor, hasMore, items: payments, onFirstPage: goToFirst, onNextPage: goToNext })
|
|
1579
1924
|
] }),
|
|
1580
|
-
showRefundDialog && selectedPayment && /* @__PURE__ */ (0,
|
|
1581
|
-
/* @__PURE__ */ (0,
|
|
1582
|
-
/* @__PURE__ */ (0,
|
|
1583
|
-
/* @__PURE__ */ (0,
|
|
1584
|
-
/* @__PURE__ */ (0,
|
|
1585
|
-
/* @__PURE__ */ (0,
|
|
1586
|
-
/* @__PURE__ */ (0,
|
|
1587
|
-
/* @__PURE__ */ (0,
|
|
1588
|
-
/* @__PURE__ */ (0,
|
|
1589
|
-
/* @__PURE__ */ (0,
|
|
1925
|
+
showRefundDialog && selectedPayment && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
1926
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => setShowRefundDialog(false) }),
|
|
1927
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "relative z-50 w-full max-w-md rounded-lg border bg-background p-6 shadow-lg", children: [
|
|
1928
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h2", { className: "text-lg font-semibold", children: "Rimborso Pagamento" }),
|
|
1929
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-sm text-muted-foreground mt-1", children: "Effettua un rimborso per il pagamento selezionato" }),
|
|
1930
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "space-y-4 py-4", children: [
|
|
1931
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "p-3 bg-muted rounded-lg", children: [
|
|
1932
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex justify-between items-center", children: [
|
|
1933
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-sm text-muted-foreground", children: "Pagamento originale" }),
|
|
1934
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "font-medium", children: (0, import_growflowbilling_admin_core6.formatCurrencyFromCents)(selectedPayment.amount, selectedPayment.currency) })
|
|
1590
1935
|
] }),
|
|
1591
|
-
selectedPayment.customer_email && /* @__PURE__ */ (0,
|
|
1936
|
+
selectedPayment.customer_email && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("p", { className: "text-sm text-muted-foreground mt-1", children: [
|
|
1592
1937
|
"Cliente: ",
|
|
1593
1938
|
selectedPayment.customer_email
|
|
1594
1939
|
] })
|
|
1595
1940
|
] }),
|
|
1596
|
-
/* @__PURE__ */ (0,
|
|
1597
|
-
/* @__PURE__ */ (0,
|
|
1598
|
-
/* @__PURE__ */ (0,
|
|
1941
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "space-y-2", children: [
|
|
1942
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { className: "text-sm font-medium leading-none", children: "Importo Rimborso" }),
|
|
1943
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.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
1944
|
] }),
|
|
1600
|
-
/* @__PURE__ */ (0,
|
|
1601
|
-
/* @__PURE__ */ (0,
|
|
1602
|
-
/* @__PURE__ */ (0,
|
|
1603
|
-
/* @__PURE__ */ (0,
|
|
1604
|
-
/* @__PURE__ */ (0,
|
|
1605
|
-
/* @__PURE__ */ (0,
|
|
1945
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "space-y-2", children: [
|
|
1946
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { className: "text-sm font-medium leading-none", children: "Motivo" }),
|
|
1947
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.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: [
|
|
1948
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: "requested_by_customer", children: "Richiesto dal cliente" }),
|
|
1949
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: "duplicate", children: "Pagamento duplicato" }),
|
|
1950
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: "fraudulent", children: "Fraudolento" })
|
|
1606
1951
|
] })
|
|
1607
1952
|
] })
|
|
1608
1953
|
] }),
|
|
1609
|
-
/* @__PURE__ */ (0,
|
|
1610
|
-
/* @__PURE__ */ (0,
|
|
1611
|
-
/* @__PURE__ */ (0,
|
|
1954
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex justify-end gap-2", children: [
|
|
1955
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.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" }),
|
|
1956
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.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
1957
|
] })
|
|
1613
1958
|
] })
|
|
1614
1959
|
] })
|
|
1615
1960
|
] });
|
|
1616
|
-
return Wrapper ? /* @__PURE__ */ (0,
|
|
1961
|
+
return Wrapper ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Wrapper, { children: content }) : content;
|
|
1617
1962
|
}
|
|
1618
1963
|
|
|
1619
1964
|
// src/pages/InvoicesPage.tsx
|
|
1620
|
-
var
|
|
1965
|
+
var import_react9 = require("react");
|
|
1621
1966
|
var import_sonner5 = require("sonner");
|
|
1622
|
-
var
|
|
1967
|
+
var import_lucide_react13 = require("lucide-react");
|
|
1623
1968
|
var import_growflowbilling_admin_core8 = require("@growflowstudio/growflowbilling-admin-core");
|
|
1624
1969
|
|
|
1625
1970
|
// src/components/invoices/InvoiceDetailsDialog.tsx
|
|
1626
|
-
var
|
|
1971
|
+
var import_lucide_react12 = require("lucide-react");
|
|
1627
1972
|
var import_growflowbilling_admin_core7 = require("@growflowstudio/growflowbilling-admin-core");
|
|
1628
|
-
var
|
|
1973
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1629
1974
|
function InvoiceDetailsDialog({ isOpen, onOpenChange, invoice }) {
|
|
1630
1975
|
if (!isOpen || !invoice) return null;
|
|
1631
|
-
return /* @__PURE__ */ (0,
|
|
1632
|
-
/* @__PURE__ */ (0,
|
|
1633
|
-
/* @__PURE__ */ (0,
|
|
1634
|
-
/* @__PURE__ */ (0,
|
|
1635
|
-
/* @__PURE__ */ (0,
|
|
1636
|
-
/* @__PURE__ */ (0,
|
|
1976
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
1977
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: () => onOpenChange(false) }),
|
|
1978
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "relative z-50 w-full max-w-lg rounded-lg border bg-background p-6 shadow-lg", children: [
|
|
1979
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col space-y-1.5 pb-4", children: [
|
|
1980
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("h2", { className: "text-lg font-semibold", children: "Dettagli Fattura" }),
|
|
1981
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm text-muted-foreground", children: invoice.number || "Fattura senza numero" })
|
|
1637
1982
|
] }),
|
|
1638
|
-
/* @__PURE__ */ (0,
|
|
1639
|
-
/* @__PURE__ */ (0,
|
|
1640
|
-
/* @__PURE__ */ (0,
|
|
1641
|
-
/* @__PURE__ */ (0,
|
|
1642
|
-
/* @__PURE__ */ (0,
|
|
1983
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "space-y-4", children: [
|
|
1984
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
1985
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
|
|
1986
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm text-muted-foreground", children: "Stato" }),
|
|
1987
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StatusBadge, { label: import_growflowbilling_admin_core7.invoiceStatusLabels[invoice.status], colorClass: import_growflowbilling_admin_core7.invoiceStatusColors[invoice.status], className: "mt-1" })
|
|
1643
1988
|
] }),
|
|
1644
|
-
/* @__PURE__ */ (0,
|
|
1645
|
-
/* @__PURE__ */ (0,
|
|
1646
|
-
/* @__PURE__ */ (0,
|
|
1989
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
|
|
1990
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm text-muted-foreground", children: "Data Creazione" }),
|
|
1991
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "font-medium mt-1", children: (0, import_growflowbilling_admin_core7.formatTimestamp)(invoice.created) })
|
|
1647
1992
|
] })
|
|
1648
1993
|
] }),
|
|
1649
|
-
/* @__PURE__ */ (0,
|
|
1650
|
-
/* @__PURE__ */ (0,
|
|
1651
|
-
/* @__PURE__ */ (0,
|
|
1652
|
-
/* @__PURE__ */ (0,
|
|
1994
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
1995
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
|
|
1996
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm text-muted-foreground", children: "Importo Dovuto" }),
|
|
1997
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "font-medium mt-1", children: (0, import_growflowbilling_admin_core7.formatCurrencyFromCents)(invoice.amount_due, invoice.currency) })
|
|
1653
1998
|
] }),
|
|
1654
|
-
/* @__PURE__ */ (0,
|
|
1655
|
-
/* @__PURE__ */ (0,
|
|
1656
|
-
/* @__PURE__ */ (0,
|
|
1999
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
|
|
2000
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm text-muted-foreground", children: "Importo Pagato" }),
|
|
2001
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "font-medium mt-1", children: (0, import_growflowbilling_admin_core7.formatCurrencyFromCents)(invoice.amount_paid, invoice.currency) })
|
|
1657
2002
|
] })
|
|
1658
2003
|
] }),
|
|
1659
|
-
invoice.amount_remaining > 0 && /* @__PURE__ */ (0,
|
|
2004
|
+
invoice.amount_remaining > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "p-3 bg-amber-50 dark:bg-amber-900/20 rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("p", { className: "text-sm text-amber-700 dark:text-amber-400", children: [
|
|
1660
2005
|
"Importo rimanente: ",
|
|
1661
|
-
/* @__PURE__ */ (0,
|
|
2006
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("strong", { children: (0, import_growflowbilling_admin_core7.formatCurrencyFromCents)(invoice.amount_remaining, invoice.currency) })
|
|
1662
2007
|
] }) }),
|
|
1663
|
-
/* @__PURE__ */ (0,
|
|
1664
|
-
/* @__PURE__ */ (0,
|
|
1665
|
-
/* @__PURE__ */ (0,
|
|
1666
|
-
invoice.customer_email && /* @__PURE__ */ (0,
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
|
|
2009
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm text-muted-foreground", children: "Cliente" }),
|
|
2010
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "font-medium mt-1", children: invoice.customer_name || "Non specificato" }),
|
|
2011
|
+
invoice.customer_email && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm text-muted-foreground", children: invoice.customer_email })
|
|
1667
2012
|
] }),
|
|
1668
|
-
invoice.lines && invoice.lines.length > 0 && /* @__PURE__ */ (0,
|
|
1669
|
-
/* @__PURE__ */ (0,
|
|
1670
|
-
/* @__PURE__ */ (0,
|
|
1671
|
-
/* @__PURE__ */ (0,
|
|
1672
|
-
/* @__PURE__ */ (0,
|
|
1673
|
-
/* @__PURE__ */ (0,
|
|
2013
|
+
invoice.lines && invoice.lines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
|
|
2014
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm text-muted-foreground mb-2", children: "Voci Fattura" }),
|
|
2015
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "border rounded-lg divide-y", children: invoice.lines.map((line) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "p-3 flex justify-between items-center", children: [
|
|
2016
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
|
|
2017
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm font-medium", children: line.description }),
|
|
2018
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
1674
2019
|
"Qt\xE0: ",
|
|
1675
2020
|
line.quantity
|
|
1676
2021
|
] })
|
|
1677
2022
|
] }),
|
|
1678
|
-
/* @__PURE__ */ (0,
|
|
2023
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "font-medium", children: (0, import_growflowbilling_admin_core7.formatCurrencyFromCents)(line.amount, line.currency) })
|
|
1679
2024
|
] }, line.id)) })
|
|
1680
2025
|
] }),
|
|
1681
|
-
invoice.hosted_invoice_url && /* @__PURE__ */ (0,
|
|
2026
|
+
invoice.hosted_invoice_url && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
1682
2027
|
"button",
|
|
1683
2028
|
{
|
|
1684
2029
|
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
2030
|
onClick: () => window.open(invoice.hosted_invoice_url, "_blank"),
|
|
1686
2031
|
children: [
|
|
1687
|
-
/* @__PURE__ */ (0,
|
|
2032
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react12.ExternalLink, { className: "w-4 h-4 mr-2" }),
|
|
1688
2033
|
"Apri su Stripe"
|
|
1689
2034
|
]
|
|
1690
2035
|
}
|
|
1691
2036
|
)
|
|
1692
2037
|
] }),
|
|
1693
|
-
/* @__PURE__ */ (0,
|
|
2038
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex justify-end pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime22.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
2039
|
] })
|
|
1695
2040
|
] });
|
|
1696
2041
|
}
|
|
1697
2042
|
|
|
1698
2043
|
// src/pages/InvoicesPage.tsx
|
|
1699
|
-
var
|
|
2044
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1700
2045
|
var statusFilterOptions3 = [
|
|
1701
2046
|
{ value: "all", label: "Tutti gli stati" },
|
|
1702
2047
|
{ value: "draft", label: "Bozze" },
|
|
@@ -1706,9 +2051,9 @@ var statusFilterOptions3 = [
|
|
|
1706
2051
|
{ value: "uncollectible", label: "Non Riscuotibili" }
|
|
1707
2052
|
];
|
|
1708
2053
|
function InvoicesPage({ wrapper: Wrapper, header }) {
|
|
1709
|
-
const [search, setSearch] = (0,
|
|
1710
|
-
const [statusFilter, setStatusFilter] = (0,
|
|
1711
|
-
const
|
|
2054
|
+
const [search, setSearch] = (0, import_react9.useState)("");
|
|
2055
|
+
const [statusFilter, setStatusFilter] = (0, import_react9.useState)("all");
|
|
2056
|
+
const { cursor, goToNext, goToFirst } = (0, import_growflowbilling_admin_core8.useCursorPagination)();
|
|
1712
2057
|
const detailsDialog = (0, import_growflowbilling_admin_core8.useDialogState)();
|
|
1713
2058
|
const voidDialog = (0, import_growflowbilling_admin_core8.useDialogState)();
|
|
1714
2059
|
const { data, isLoading, refetch } = (0, import_growflowbilling_admin_core8.useAdminInvoices)({
|
|
@@ -1736,76 +2081,76 @@ function InvoicesPage({ wrapper: Wrapper, header }) {
|
|
|
1736
2081
|
if (invoice.invoice_pdf) window.open(invoice.invoice_pdf, "_blank");
|
|
1737
2082
|
else if (invoice.hosted_invoice_url) window.open(invoice.hosted_invoice_url, "_blank");
|
|
1738
2083
|
};
|
|
1739
|
-
const content = /* @__PURE__ */ (0,
|
|
2084
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
1740
2085
|
header,
|
|
1741
|
-
/* @__PURE__ */ (0,
|
|
1742
|
-
/* @__PURE__ */ (0,
|
|
2086
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "space-y-4", children: [
|
|
2087
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1743
2088
|
FilterBar,
|
|
1744
2089
|
{
|
|
1745
2090
|
searchPlaceholder: "Cerca per email o numero fattura...",
|
|
1746
2091
|
searchValue: search,
|
|
1747
2092
|
onSearchChange: (v) => {
|
|
1748
2093
|
setSearch(v);
|
|
1749
|
-
|
|
2094
|
+
goToFirst();
|
|
1750
2095
|
},
|
|
1751
2096
|
statusFilter,
|
|
1752
2097
|
onStatusFilterChange: (v) => {
|
|
1753
2098
|
setStatusFilter(v);
|
|
1754
|
-
|
|
2099
|
+
goToFirst();
|
|
1755
2100
|
},
|
|
1756
2101
|
statusOptions: statusFilterOptions3,
|
|
1757
2102
|
onRefresh: refetch
|
|
1758
2103
|
}
|
|
1759
2104
|
),
|
|
1760
|
-
/* @__PURE__ */ (0,
|
|
1761
|
-
/* @__PURE__ */ (0,
|
|
1762
|
-
/* @__PURE__ */ (0,
|
|
2105
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
2106
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react13.FileText, { className: "w-4 h-4" }),
|
|
2107
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { children: [
|
|
1763
2108
|
invoices.length,
|
|
1764
2109
|
" fatture visualizzate"
|
|
1765
2110
|
] })
|
|
1766
2111
|
] }),
|
|
1767
|
-
/* @__PURE__ */ (0,
|
|
1768
|
-
/* @__PURE__ */ (0,
|
|
1769
|
-
/* @__PURE__ */ (0,
|
|
1770
|
-
/* @__PURE__ */ (0,
|
|
1771
|
-
/* @__PURE__ */ (0,
|
|
1772
|
-
/* @__PURE__ */ (0,
|
|
1773
|
-
/* @__PURE__ */ (0,
|
|
1774
|
-
/* @__PURE__ */ (0,
|
|
1775
|
-
/* @__PURE__ */ (0,
|
|
1776
|
-
/* @__PURE__ */ (0,
|
|
2112
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "rounded-lg border bg-card shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("table", { className: "w-full caption-bottom text-sm", children: [
|
|
2113
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("thead", { className: "[&_tr]:border-b", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
|
|
2114
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Numero" }),
|
|
2115
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Stato" }),
|
|
2116
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Importo Dovuto" }),
|
|
2117
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Importo Pagato" }),
|
|
2118
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Cliente" }),
|
|
2119
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Data" }),
|
|
2120
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { className: "h-12 px-4 text-left font-medium text-muted-foreground", children: "Scadenza" }),
|
|
2121
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { className: "h-12 px-4 text-right font-medium text-muted-foreground", children: "Azioni" })
|
|
1777
2122
|
] }) }),
|
|
1778
|
-
/* @__PURE__ */ (0,
|
|
1779
|
-
/* @__PURE__ */ (0,
|
|
1780
|
-
/* @__PURE__ */ (0,
|
|
1781
|
-
/* @__PURE__ */ (0,
|
|
1782
|
-
/* @__PURE__ */ (0,
|
|
1783
|
-
/* @__PURE__ */ (0,
|
|
1784
|
-
/* @__PURE__ */ (0,
|
|
1785
|
-
inv.customer_email && /* @__PURE__ */ (0,
|
|
2123
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("tbody", { children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SkeletonRows, { rows: 5, columns: 8 }) : invoices.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime23.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_runtime23.jsxs)("tr", { className: "border-b hover:bg-muted/50", children: [
|
|
2124
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "p-4 font-medium", children: inv.number || /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-muted-foreground", children: "-" }) }),
|
|
2125
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(StatusBadge, { label: import_growflowbilling_admin_core8.invoiceStatusLabels[inv.status], colorClass: import_growflowbilling_admin_core8.invoiceStatusColors[inv.status] }) }),
|
|
2126
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "p-4 font-medium", children: (0, import_growflowbilling_admin_core8.formatCurrencyFromCents)(inv.amount_due, inv.currency) }),
|
|
2127
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "p-4", children: (0, import_growflowbilling_admin_core8.formatCurrencyFromCents)(inv.amount_paid, inv.currency) }),
|
|
2128
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col", children: [
|
|
2129
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: inv.customer_name || "-" }),
|
|
2130
|
+
inv.customer_email && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-xs text-muted-foreground", children: inv.customer_email })
|
|
1786
2131
|
] }) }),
|
|
1787
|
-
/* @__PURE__ */ (0,
|
|
1788
|
-
/* @__PURE__ */ (0,
|
|
1789
|
-
/* @__PURE__ */ (0,
|
|
1790
|
-
/* @__PURE__ */ (0,
|
|
1791
|
-
(inv.invoice_pdf || inv.hosted_invoice_url) && /* @__PURE__ */ (0,
|
|
1792
|
-
inv.status === "open" && /* @__PURE__ */ (0,
|
|
1793
|
-
(inv.status === "draft" || inv.status === "open") && /* @__PURE__ */ (0,
|
|
2132
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "p-4", children: (0, import_growflowbilling_admin_core8.formatTimestamp)(inv.created) }),
|
|
2133
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "p-4", children: (0, import_growflowbilling_admin_core8.formatTimestamp)(inv.due_date) }),
|
|
2134
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "p-4 text-right", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex justify-end gap-1", children: [
|
|
2135
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.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_runtime23.jsx)(import_lucide_react13.Eye, { className: "w-4 h-4" }) }),
|
|
2136
|
+
(inv.invoice_pdf || inv.hosted_invoice_url) && /* @__PURE__ */ (0, import_jsx_runtime23.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_runtime23.jsx)(import_lucide_react13.Download, { className: "w-4 h-4" }) }),
|
|
2137
|
+
inv.status === "open" && /* @__PURE__ */ (0, import_jsx_runtime23.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_runtime23.jsx)(import_lucide_react13.Send, { className: "w-4 h-4" }) }),
|
|
2138
|
+
(inv.status === "draft" || inv.status === "open") && /* @__PURE__ */ (0, import_jsx_runtime23.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_runtime23.jsx)(import_lucide_react13.XCircle, { className: "w-4 h-4" }) })
|
|
1794
2139
|
] }) })
|
|
1795
2140
|
] }, inv.id)) })
|
|
1796
2141
|
] }) }) }),
|
|
1797
|
-
/* @__PURE__ */ (0,
|
|
2142
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CursorPagination, { cursor, hasMore, items: invoices, onFirstPage: goToFirst, onNextPage: goToNext })
|
|
1798
2143
|
] }),
|
|
1799
|
-
/* @__PURE__ */ (0,
|
|
1800
|
-
/* @__PURE__ */ (0,
|
|
2144
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(InvoiceDetailsDialog, { isOpen: detailsDialog.isOpen, onOpenChange: (open) => !open && detailsDialog.close(), invoice: detailsDialog.data }),
|
|
2145
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1801
2146
|
DeleteConfirmDialog,
|
|
1802
2147
|
{
|
|
1803
2148
|
isOpen: voidDialog.isOpen,
|
|
1804
2149
|
onOpenChange: (open) => !open && voidDialog.close(),
|
|
1805
2150
|
title: "Annullare Fattura?",
|
|
1806
|
-
description: /* @__PURE__ */ (0,
|
|
2151
|
+
description: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
1807
2152
|
"Stai per annullare la fattura ",
|
|
1808
|
-
/* @__PURE__ */ (0,
|
|
2153
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("strong", { children: voidDialog.data?.number || voidDialog.data?.id }),
|
|
1809
2154
|
". Una fattura annullata non pu\xF2 essere riattivata."
|
|
1810
2155
|
] }),
|
|
1811
2156
|
onConfirm: handleVoidConfirm,
|
|
@@ -1814,279 +2159,363 @@ function InvoicesPage({ wrapper: Wrapper, header }) {
|
|
|
1814
2159
|
}
|
|
1815
2160
|
)
|
|
1816
2161
|
] });
|
|
1817
|
-
return Wrapper ? /* @__PURE__ */ (0,
|
|
2162
|
+
return Wrapper ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Wrapper, { children: content }) : content;
|
|
1818
2163
|
}
|
|
1819
2164
|
|
|
1820
2165
|
// src/pages/FeaturesPage.tsx
|
|
1821
|
-
var
|
|
1822
|
-
var
|
|
1823
|
-
var
|
|
2166
|
+
var import_react10 = require("react");
|
|
2167
|
+
var import_lucide_react15 = require("lucide-react");
|
|
2168
|
+
var import_growflowbilling_admin_core10 = require("@growflowstudio/growflowbilling-admin-core");
|
|
2169
|
+
var import_sonner7 = require("sonner");
|
|
2170
|
+
|
|
2171
|
+
// src/components/features/FeatureFormModal.tsx
|
|
1824
2172
|
var import_growflowbilling_admin_core9 = require("@growflowstudio/growflowbilling-admin-core");
|
|
1825
|
-
var
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
const
|
|
1829
|
-
const [deletingFeature, setDeletingFeature] = (0, import_react8.useState)(null);
|
|
1830
|
-
const { data, isLoading } = (0, import_growflowbilling_admin_core9.useAdminFeatures)();
|
|
2173
|
+
var import_sonner6 = require("sonner");
|
|
2174
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
2175
|
+
function FeatureFormModal({ feature, open, onClose, onSuccess }) {
|
|
2176
|
+
const isEditMode = feature !== null;
|
|
1831
2177
|
const createMutation = (0, import_growflowbilling_admin_core9.useCreateFeature)();
|
|
1832
2178
|
const updateMutation = (0, import_growflowbilling_admin_core9.useUpdateFeature)();
|
|
1833
|
-
const
|
|
1834
|
-
const
|
|
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) => {
|
|
2179
|
+
const isPending = createMutation.isPending || updateMutation.isPending;
|
|
2180
|
+
const handleSubmit = (e) => {
|
|
1856
2181
|
e.preventDefault();
|
|
1857
|
-
if (!editingFeature) return;
|
|
1858
2182
|
const formData = new FormData(e.currentTarget);
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
2183
|
+
if (isEditMode) {
|
|
2184
|
+
const updateData = {
|
|
2185
|
+
name: formData.get("name"),
|
|
2186
|
+
description: formData.get("description") || void 0,
|
|
2187
|
+
icon: formData.get("icon") || void 0,
|
|
2188
|
+
category: formData.get("category") || void 0,
|
|
2189
|
+
is_available: formData.get("is_available") === "on",
|
|
2190
|
+
billing_product_id: formData.get("billing_product_id") || void 0
|
|
2191
|
+
};
|
|
2192
|
+
updateMutation.mutate(
|
|
2193
|
+
{ id: feature.id, data: updateData },
|
|
2194
|
+
{
|
|
2195
|
+
onSuccess: () => {
|
|
2196
|
+
import_sonner6.toast.success("Feature aggiornata con successo");
|
|
2197
|
+
onSuccess();
|
|
2198
|
+
},
|
|
2199
|
+
onError: (err) => import_sonner6.toast.error(`Errore: ${err.message}`)
|
|
2200
|
+
}
|
|
2201
|
+
);
|
|
2202
|
+
} else {
|
|
2203
|
+
const createData = {
|
|
2204
|
+
slug: formData.get("slug"),
|
|
2205
|
+
name: formData.get("name"),
|
|
2206
|
+
description: formData.get("description") || void 0,
|
|
2207
|
+
icon: formData.get("icon") || void 0,
|
|
2208
|
+
category: formData.get("category") || void 0,
|
|
2209
|
+
is_available: formData.get("is_available") === "on",
|
|
2210
|
+
billing_product_id: formData.get("billing_product_id") || void 0
|
|
2211
|
+
};
|
|
2212
|
+
createMutation.mutate(createData, {
|
|
2213
|
+
onSuccess: () => {
|
|
2214
|
+
import_sonner6.toast.success("Feature creata con successo");
|
|
2215
|
+
onSuccess();
|
|
2216
|
+
},
|
|
2217
|
+
onError: (err) => import_sonner6.toast.error(`Errore: ${err.message}`)
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
1874
2220
|
};
|
|
2221
|
+
if (!open) return null;
|
|
2222
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
2223
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "fixed inset-0 bg-black/80", onClick: onClose }),
|
|
2224
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.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_runtime24.jsxs)("form", { onSubmit: handleSubmit, children: [
|
|
2225
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h2", { className: "text-lg font-semibold", children: isEditMode ? "Modifica Feature" : "Crea Nuova Feature" }),
|
|
2226
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-sm text-muted-foreground mt-1", children: isEditMode ? `Aggiorna i dettagli della feature "${feature.name}".` : "Configura una nuova feature disponibile." }),
|
|
2227
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "space-y-4 py-4", children: [
|
|
2228
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "space-y-2", children: [
|
|
2229
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "slug", children: isEditMode ? "Slug (non modificabile)" : "Slug *" }),
|
|
2230
|
+
isEditMode ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2231
|
+
"input",
|
|
2232
|
+
{
|
|
2233
|
+
value: feature.slug,
|
|
2234
|
+
disabled: true,
|
|
2235
|
+
className: "flex h-10 w-full rounded-md border border-input bg-muted px-3 py-2 text-sm"
|
|
2236
|
+
}
|
|
2237
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
2238
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2239
|
+
"input",
|
|
2240
|
+
{
|
|
2241
|
+
id: "slug",
|
|
2242
|
+
name: "slug",
|
|
2243
|
+
required: true,
|
|
2244
|
+
placeholder: "ottimizza-dati-prodotti",
|
|
2245
|
+
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"
|
|
2246
|
+
}
|
|
2247
|
+
),
|
|
2248
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-xs text-muted-foreground", children: "Identificatore univoco (lowercase, trattini)" })
|
|
2249
|
+
] })
|
|
2250
|
+
] }),
|
|
2251
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "space-y-2", children: [
|
|
2252
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "name", children: "Nome *" }),
|
|
2253
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2254
|
+
"input",
|
|
2255
|
+
{
|
|
2256
|
+
id: isEditMode ? "edit-name" : "name",
|
|
2257
|
+
name: "name",
|
|
2258
|
+
required: true,
|
|
2259
|
+
defaultValue: feature?.name ?? "",
|
|
2260
|
+
placeholder: "Ottimizza Dati Prodotti",
|
|
2261
|
+
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"
|
|
2262
|
+
}
|
|
2263
|
+
)
|
|
2264
|
+
] }),
|
|
2265
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "space-y-2", children: [
|
|
2266
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "description", children: "Descrizione" }),
|
|
2267
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2268
|
+
"textarea",
|
|
2269
|
+
{
|
|
2270
|
+
id: isEditMode ? "edit-description" : "description",
|
|
2271
|
+
name: "description",
|
|
2272
|
+
rows: 3,
|
|
2273
|
+
defaultValue: feature?.description ?? "",
|
|
2274
|
+
placeholder: "Descrizione della feature...",
|
|
2275
|
+
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"
|
|
2276
|
+
}
|
|
2277
|
+
)
|
|
2278
|
+
] }),
|
|
2279
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
2280
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "space-y-2", children: [
|
|
2281
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "icon", children: "Icona Lucide" }),
|
|
2282
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2283
|
+
"input",
|
|
2284
|
+
{
|
|
2285
|
+
id: isEditMode ? "edit-icon" : "icon",
|
|
2286
|
+
name: "icon",
|
|
2287
|
+
defaultValue: feature?.icon ?? "",
|
|
2288
|
+
placeholder: "Package",
|
|
2289
|
+
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"
|
|
2290
|
+
}
|
|
2291
|
+
)
|
|
2292
|
+
] }),
|
|
2293
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "space-y-2", children: [
|
|
2294
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "category", children: "Categoria" }),
|
|
2295
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2296
|
+
"input",
|
|
2297
|
+
{
|
|
2298
|
+
id: isEditMode ? "edit-category" : "category",
|
|
2299
|
+
name: "category",
|
|
2300
|
+
defaultValue: feature?.category ?? "",
|
|
2301
|
+
placeholder: "ai, limits, integration...",
|
|
2302
|
+
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"
|
|
2303
|
+
}
|
|
2304
|
+
)
|
|
2305
|
+
] })
|
|
2306
|
+
] }),
|
|
2307
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "space-y-2", children: [
|
|
2308
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { className: "text-sm font-medium leading-none", htmlFor: "billing_product_id", children: "Billing Product ID (opzionale)" }),
|
|
2309
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2310
|
+
"input",
|
|
2311
|
+
{
|
|
2312
|
+
id: isEditMode ? "edit-billing_product_id" : "billing_product_id",
|
|
2313
|
+
name: "billing_product_id",
|
|
2314
|
+
defaultValue: feature?.billing_product_id ?? "",
|
|
2315
|
+
placeholder: "UUID del prodotto billing",
|
|
2316
|
+
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"
|
|
2317
|
+
}
|
|
2318
|
+
),
|
|
2319
|
+
!isEditMode && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-xs text-muted-foreground", children: "Se collegato a un prodotto, la feature viene attivata automaticamente" })
|
|
2320
|
+
] }),
|
|
2321
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2322
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2323
|
+
"input",
|
|
2324
|
+
{
|
|
2325
|
+
type: "checkbox",
|
|
2326
|
+
id: isEditMode ? "edit-is_available" : "is_available",
|
|
2327
|
+
name: "is_available",
|
|
2328
|
+
defaultChecked: feature?.is_available ?? true,
|
|
2329
|
+
className: "rounded border-input"
|
|
2330
|
+
}
|
|
2331
|
+
),
|
|
2332
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2333
|
+
"label",
|
|
2334
|
+
{
|
|
2335
|
+
className: "text-sm font-medium leading-none",
|
|
2336
|
+
htmlFor: isEditMode ? "edit-is_available" : "is_available",
|
|
2337
|
+
children: "Feature disponibile"
|
|
2338
|
+
}
|
|
2339
|
+
)
|
|
2340
|
+
] })
|
|
2341
|
+
] }),
|
|
2342
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex justify-end gap-2", children: [
|
|
2343
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2344
|
+
"button",
|
|
2345
|
+
{
|
|
2346
|
+
type: "button",
|
|
2347
|
+
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",
|
|
2348
|
+
onClick: onClose,
|
|
2349
|
+
children: "Annulla"
|
|
2350
|
+
}
|
|
2351
|
+
),
|
|
2352
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2353
|
+
"button",
|
|
2354
|
+
{
|
|
2355
|
+
type: "submit",
|
|
2356
|
+
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",
|
|
2357
|
+
disabled: isPending,
|
|
2358
|
+
children: isPending ? isEditMode ? "Salvataggio..." : "Creazione..." : isEditMode ? "Salva Modifiche" : "Crea Feature"
|
|
2359
|
+
}
|
|
2360
|
+
)
|
|
2361
|
+
] })
|
|
2362
|
+
] }) })
|
|
2363
|
+
] });
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
// src/components/features/FeatureListItem.tsx
|
|
2367
|
+
var import_lucide_react14 = require("lucide-react");
|
|
2368
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
2369
|
+
function FeatureListItem({ feature, onEdit, onDelete }) {
|
|
2370
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "border rounded-lg p-4 hover:bg-muted/50 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-start justify-between", children: [
|
|
2371
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex-1", children: [
|
|
2372
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
2373
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-semibold", children: feature.name }),
|
|
2374
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("code", { className: "text-xs bg-muted px-2 py-0.5 rounded", children: feature.slug }),
|
|
2375
|
+
!feature.is_available && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(StatusBadge, { label: "Disattivata", colorClass: "bg-secondary text-secondary-foreground border-transparent" }),
|
|
2376
|
+
feature.billing_product_id && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(StatusBadge, { label: "Con Billing", colorClass: "border" })
|
|
2377
|
+
] }),
|
|
2378
|
+
feature.description && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-sm text-muted-foreground mb-2", children: feature.description }),
|
|
2379
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center gap-4 text-xs text-muted-foreground", children: [
|
|
2380
|
+
feature.category && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { children: [
|
|
2381
|
+
"Categoria: ",
|
|
2382
|
+
feature.category
|
|
2383
|
+
] }),
|
|
2384
|
+
feature.icon && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { children: [
|
|
2385
|
+
"Icona: ",
|
|
2386
|
+
feature.icon
|
|
2387
|
+
] })
|
|
2388
|
+
] })
|
|
2389
|
+
] }),
|
|
2390
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center gap-1", children: [
|
|
2391
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
2392
|
+
"button",
|
|
2393
|
+
{
|
|
2394
|
+
className: "inline-flex items-center justify-center rounded-md h-9 w-9 hover:bg-accent",
|
|
2395
|
+
onClick: () => onEdit(feature),
|
|
2396
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react14.Pencil, { className: "w-4 h-4" })
|
|
2397
|
+
}
|
|
2398
|
+
),
|
|
2399
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
2400
|
+
"button",
|
|
2401
|
+
{
|
|
2402
|
+
className: "inline-flex items-center justify-center rounded-md h-9 w-9 text-destructive hover:bg-accent",
|
|
2403
|
+
onClick: () => onDelete(feature),
|
|
2404
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react14.Trash2, { className: "w-4 h-4" })
|
|
2405
|
+
}
|
|
2406
|
+
)
|
|
2407
|
+
] })
|
|
2408
|
+
] }) });
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
// src/pages/FeaturesPage.tsx
|
|
2412
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
2413
|
+
function FeaturesPage({ wrapper: Wrapper, header }) {
|
|
2414
|
+
const [modalFeature, setModalFeature] = (0, import_react10.useState)(void 0);
|
|
2415
|
+
const [deletingFeature, setDeletingFeature] = (0, import_react10.useState)(null);
|
|
2416
|
+
const isModalOpen = modalFeature !== void 0;
|
|
2417
|
+
const { data, isLoading, refetch } = (0, import_growflowbilling_admin_core10.useAdminFeatures)();
|
|
2418
|
+
const deleteMutation = (0, import_growflowbilling_admin_core10.useDeleteFeature)();
|
|
2419
|
+
const features = data?.features || [];
|
|
1875
2420
|
const handleDeleteConfirm = () => {
|
|
1876
2421
|
if (deletingFeature) {
|
|
1877
2422
|
deleteMutation.mutate(deletingFeature.id, {
|
|
1878
2423
|
onSuccess: () => {
|
|
1879
|
-
|
|
2424
|
+
import_sonner7.toast.success("Feature eliminata con successo");
|
|
1880
2425
|
setDeletingFeature(null);
|
|
1881
2426
|
},
|
|
1882
|
-
onError: (err) =>
|
|
2427
|
+
onError: (err) => import_sonner7.toast.error(`Errore: ${err.message}`)
|
|
1883
2428
|
});
|
|
1884
2429
|
}
|
|
1885
2430
|
};
|
|
1886
|
-
const
|
|
2431
|
+
const handleModalSuccess = () => {
|
|
2432
|
+
refetch();
|
|
2433
|
+
setModalFeature(void 0);
|
|
2434
|
+
};
|
|
2435
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
|
|
1887
2436
|
header,
|
|
1888
|
-
/* @__PURE__ */ (0,
|
|
1889
|
-
/* @__PURE__ */ (0,
|
|
1890
|
-
/* @__PURE__ */ (0,
|
|
1891
|
-
/* @__PURE__ */ (0,
|
|
1892
|
-
/* @__PURE__ */ (0,
|
|
2437
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "space-y-4", children: [
|
|
2438
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2439
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
2440
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react15.Package, { className: "w-4 h-4" }),
|
|
2441
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { children: [
|
|
1893
2442
|
features.length,
|
|
1894
2443
|
" feature",
|
|
1895
2444
|
features.length !== 1 ? "s" : "",
|
|
1896
2445
|
" configurate"
|
|
1897
2446
|
] })
|
|
1898
2447
|
] }),
|
|
1899
|
-
/* @__PURE__ */ (0,
|
|
2448
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
1900
2449
|
"button",
|
|
1901
2450
|
{
|
|
1902
2451
|
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: () =>
|
|
2452
|
+
onClick: () => setModalFeature(null),
|
|
1904
2453
|
children: [
|
|
1905
|
-
/* @__PURE__ */ (0,
|
|
2454
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react15.Plus, { className: "w-4 h-4 mr-2" }),
|
|
1906
2455
|
"Nuova Feature"
|
|
1907
2456
|
]
|
|
1908
2457
|
}
|
|
1909
2458
|
)
|
|
1910
2459
|
] }),
|
|
1911
|
-
/* @__PURE__ */ (0,
|
|
1912
|
-
/* @__PURE__ */ (0,
|
|
1913
|
-
/* @__PURE__ */ (0,
|
|
1914
|
-
/* @__PURE__ */ (0,
|
|
2460
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "rounded-lg border bg-card shadow-sm", children: [
|
|
2461
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "p-4 border-b", children: [
|
|
2462
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("h3", { className: "text-base font-semibold flex items-center gap-2", children: [
|
|
2463
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react15.Package, { className: "w-5 h-5" }),
|
|
1915
2464
|
"Features Disponibili"
|
|
1916
2465
|
] }),
|
|
1917
|
-
/* @__PURE__ */ (0,
|
|
2466
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-sm text-muted-foreground mt-1", children: "Le features possono essere collegate a prodotti billing o attivate manualmente." })
|
|
1918
2467
|
] }),
|
|
1919
|
-
/* @__PURE__ */ (0,
|
|
1920
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
1921
|
-
/* @__PURE__ */ (0,
|
|
1922
|
-
/* @__PURE__ */ (0,
|
|
1923
|
-
/* @__PURE__ */ (0,
|
|
2468
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "p-4", children: [
|
|
2469
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "space-y-3", children: [1, 2, 3].map((i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "h-20 bg-muted animate-pulse rounded-lg" }, i)) }) : features.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "text-center py-12 text-muted-foreground", children: [
|
|
2470
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react15.Package, { className: "mx-auto h-12 w-12 mb-4 opacity-50" }),
|
|
2471
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { children: "Nessuna feature configurata" }),
|
|
2472
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1924
2473
|
"button",
|
|
1925
2474
|
{
|
|
1926
2475
|
className: "text-sm text-primary hover:underline mt-2",
|
|
1927
|
-
onClick: () =>
|
|
2476
|
+
onClick: () => setModalFeature(null),
|
|
1928
2477
|
children: "Crea la prima feature"
|
|
1929
2478
|
}
|
|
1930
2479
|
)
|
|
1931
|
-
] }) : /* @__PURE__ */ (0,
|
|
1932
|
-
|
|
2480
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "space-y-3", children: features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2481
|
+
FeatureListItem,
|
|
1933
2482
|
{
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
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
|
-
] })
|
|
2483
|
+
feature,
|
|
2484
|
+
onEdit: (f) => setModalFeature(f),
|
|
2485
|
+
onDelete: (f) => setDeletingFeature(f)
|
|
1974
2486
|
},
|
|
1975
2487
|
feature.id
|
|
1976
2488
|
)) }),
|
|
1977
|
-
/* @__PURE__ */ (0,
|
|
1978
|
-
/* @__PURE__ */ (0,
|
|
1979
|
-
/* @__PURE__ */ (0,
|
|
1980
|
-
/* @__PURE__ */ (0,
|
|
2489
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "mt-6 flex gap-3 items-start rounded-lg border p-3 text-sm", children: [
|
|
2490
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react15.AlertCircle, { className: "w-4 h-4 mt-0.5 text-muted-foreground shrink-0" }),
|
|
2491
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("p", { className: "text-muted-foreground text-xs", children: [
|
|
2492
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("strong", { children: "Nota:" }),
|
|
1981
2493
|
" Features con un billing_product_id vengono attivate automaticamente quando il cliente sottoscrive il piano corrispondente."
|
|
1982
2494
|
] })
|
|
1983
2495
|
] })
|
|
1984
2496
|
] })
|
|
1985
2497
|
] })
|
|
1986
2498
|
] }),
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
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)(
|
|
2499
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2500
|
+
FeatureFormModal,
|
|
2501
|
+
{
|
|
2502
|
+
feature: isModalOpen ? modalFeature ?? null : null,
|
|
2503
|
+
open: isModalOpen,
|
|
2504
|
+
onClose: () => setModalFeature(void 0),
|
|
2505
|
+
onSuccess: handleModalSuccess
|
|
2506
|
+
}
|
|
2507
|
+
),
|
|
2508
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2080
2509
|
DeleteConfirmDialog,
|
|
2081
2510
|
{
|
|
2082
2511
|
isOpen: !!deletingFeature,
|
|
2083
2512
|
onOpenChange: (open) => !open && setDeletingFeature(null),
|
|
2084
2513
|
title: "Eliminare Feature?",
|
|
2085
|
-
description: /* @__PURE__ */ (0,
|
|
2514
|
+
description: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
|
|
2086
2515
|
"Stai per eliminare la feature ",
|
|
2087
|
-
/* @__PURE__ */ (0,
|
|
2516
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("strong", { children: deletingFeature?.name }),
|
|
2088
2517
|
" (",
|
|
2089
|
-
/* @__PURE__ */ (0,
|
|
2518
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("code", { children: deletingFeature?.slug }),
|
|
2090
2519
|
"). Questa azione non pu\xF2 essere annullata."
|
|
2091
2520
|
] }),
|
|
2092
2521
|
onConfirm: handleDeleteConfirm,
|
|
@@ -2095,7 +2524,7 @@ function FeaturesPage({ wrapper: Wrapper, header }) {
|
|
|
2095
2524
|
}
|
|
2096
2525
|
)
|
|
2097
2526
|
] });
|
|
2098
|
-
return Wrapper ? /* @__PURE__ */ (0,
|
|
2527
|
+
return Wrapper ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Wrapper, { children: content }) : content;
|
|
2099
2528
|
}
|
|
2100
2529
|
|
|
2101
2530
|
// src/nav/billingNavItems.ts
|
|
@@ -2127,6 +2556,8 @@ function getBillingNavItems(modules, basePath) {
|
|
|
2127
2556
|
BillingAdminProvider,
|
|
2128
2557
|
CursorPagination,
|
|
2129
2558
|
DeleteConfirmDialog,
|
|
2559
|
+
FeatureFormModal,
|
|
2560
|
+
FeatureListItem,
|
|
2130
2561
|
FeaturesPage,
|
|
2131
2562
|
FilterBar,
|
|
2132
2563
|
InvoiceDetailsDialog,
|
|
@@ -2134,7 +2565,11 @@ function getBillingNavItems(modules, basePath) {
|
|
|
2134
2565
|
LimitInput,
|
|
2135
2566
|
PLAN_COLORS,
|
|
2136
2567
|
PaymentsPage,
|
|
2568
|
+
PlanDetailsSection,
|
|
2569
|
+
PlanFeaturesSection,
|
|
2137
2570
|
PlanFormDialog,
|
|
2571
|
+
PlanLimitsSection,
|
|
2572
|
+
PlanPricingSection,
|
|
2138
2573
|
PlansPage,
|
|
2139
2574
|
PlansTable,
|
|
2140
2575
|
ProductFormDialog,
|
|
@@ -2142,6 +2577,7 @@ function getBillingNavItems(modules, basePath) {
|
|
|
2142
2577
|
SkeletonRows,
|
|
2143
2578
|
StatusBadge,
|
|
2144
2579
|
SubscriptionsPage,
|
|
2580
|
+
ToggleSwitch,
|
|
2145
2581
|
createDefaultPlanFormData,
|
|
2146
2582
|
defaultProductFormData,
|
|
2147
2583
|
getBillingNavItems,
|