@growflowstudio/growflowbooking-admin-ui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1550 @@
1
+ // src/provider.tsx
2
+ import React, { useMemo } from "react";
3
+ import {
4
+ BookingAdminClientContext,
5
+ createBookingAdminClient
6
+ } from "@growflowstudio/growflowbooking-admin-core";
7
+ import { jsx } from "react/jsx-runtime";
8
+ var DEFAULT_MODULES = {
9
+ tenants: true,
10
+ customers: true,
11
+ subscriptionPlans: true,
12
+ billingPlans: true,
13
+ billingSubscriptions: true,
14
+ settings: true
15
+ };
16
+ var BookingAdminConfigContext = React.createContext(null);
17
+ function useBookingAdminConfig() {
18
+ const config = React.useContext(BookingAdminConfigContext);
19
+ if (!config) {
20
+ throw new Error(
21
+ "useBookingAdminConfig must be used within a BookingAdminProvider."
22
+ );
23
+ }
24
+ return config;
25
+ }
26
+ function useEnabledModules() {
27
+ const config = useBookingAdminConfig();
28
+ return { ...DEFAULT_MODULES, ...config.modules };
29
+ }
30
+ function BookingAdminProvider({ config, children }) {
31
+ const client = useMemo(
32
+ () => createBookingAdminClient({
33
+ basePath: config.basePath,
34
+ fetcher: config.fetcher
35
+ }),
36
+ [config.basePath, config.fetcher]
37
+ );
38
+ return /* @__PURE__ */ jsx(BookingAdminConfigContext.Provider, { value: config, children: /* @__PURE__ */ jsx(BookingAdminClientContext.Provider, { value: client, children }) });
39
+ }
40
+
41
+ // src/pages/TenantsPage.tsx
42
+ import { useState as useState2 } from "react";
43
+ import { toast } from "sonner";
44
+ import { Plus, Building2 } from "lucide-react";
45
+ import {
46
+ useAdminTenants,
47
+ useAdminTenant,
48
+ useCreateTenant,
49
+ useUpdateTenant,
50
+ useDeleteTenant
51
+ } from "@growflowstudio/growflowbooking-admin-core";
52
+
53
+ // src/components/tenants/TenantsTable.tsx
54
+ import { Pencil, Trash2 } from "lucide-react";
55
+
56
+ // src/primitives/utils.ts
57
+ import { clsx } from "clsx";
58
+ import { twMerge } from "tailwind-merge";
59
+ function cn(...inputs) {
60
+ return twMerge(clsx(inputs));
61
+ }
62
+
63
+ // src/components/shared/StatusBadge.tsx
64
+ import { jsx as jsx2 } from "react/jsx-runtime";
65
+ function StatusBadge({ label, colorClass, className }) {
66
+ return /* @__PURE__ */ jsx2(
67
+ "span",
68
+ {
69
+ className: cn(
70
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors",
71
+ colorClass,
72
+ className
73
+ ),
74
+ children: label
75
+ }
76
+ );
77
+ }
78
+
79
+ // src/components/shared/SkeletonRows.tsx
80
+ import { Fragment, jsx as jsx3 } from "react/jsx-runtime";
81
+ function SkeletonRows({ rows = 5, columns = 1 }) {
82
+ return /* @__PURE__ */ jsx3(Fragment, { children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsx3("td", { colSpan: columns, className: "p-4", children: /* @__PURE__ */ jsx3("div", { className: "h-10 w-full animate-pulse rounded-md bg-muted" }) }) }, i)) });
83
+ }
84
+
85
+ // src/components/tenants/TenantsTable.tsx
86
+ import {
87
+ tenantStatusColors,
88
+ tenantStatusLabels,
89
+ tenantPlanColors,
90
+ tenantPlanLabels,
91
+ formatDate
92
+ } from "@growflowstudio/growflowbooking-admin-core";
93
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
94
+ function TenantsTable({ tenants, isLoading, onEdit, onDelete, onView }) {
95
+ return /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
96
+ /* @__PURE__ */ jsx4("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b", children: [
97
+ /* @__PURE__ */ jsx4("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Nome" }),
98
+ /* @__PURE__ */ jsx4("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Slug" }),
99
+ /* @__PURE__ */ jsx4("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Piano" }),
100
+ /* @__PURE__ */ jsx4("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Stato" }),
101
+ /* @__PURE__ */ jsx4("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Email" }),
102
+ /* @__PURE__ */ jsx4("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Creato" }),
103
+ /* @__PURE__ */ jsx4("th", { className: "h-12 px-4 text-right align-middle font-medium text-muted-foreground", children: "Azioni" })
104
+ ] }) }),
105
+ /* @__PURE__ */ jsx4("tbody", { children: isLoading ? /* @__PURE__ */ jsx4(SkeletonRows, { rows: 5, columns: 7 }) : tenants.length === 0 ? /* @__PURE__ */ jsx4("tr", { children: /* @__PURE__ */ jsx4("td", { colSpan: 7, className: "h-24 text-center text-muted-foreground", children: "Nessun tenant trovato." }) }) : tenants.map((tenant) => /* @__PURE__ */ jsxs("tr", { className: "border-b hover:bg-muted/50 cursor-pointer", onClick: () => onView(tenant), children: [
106
+ /* @__PURE__ */ jsx4("td", { className: "p-4 font-medium", children: tenant.name }),
107
+ /* @__PURE__ */ jsx4("td", { className: "p-4", children: /* @__PURE__ */ jsx4("code", { className: "text-xs bg-muted px-1.5 py-0.5 rounded", children: tenant.slug }) }),
108
+ /* @__PURE__ */ jsx4("td", { className: "p-4", children: /* @__PURE__ */ jsx4(StatusBadge, { label: tenantPlanLabels[tenant.plan], colorClass: tenantPlanColors[tenant.plan] }) }),
109
+ /* @__PURE__ */ jsx4("td", { className: "p-4", children: /* @__PURE__ */ jsx4(
110
+ StatusBadge,
111
+ {
112
+ label: tenant.is_active ? tenantStatusLabels.active : tenantStatusLabels.inactive,
113
+ colorClass: tenant.is_active ? tenantStatusColors.active : tenantStatusColors.inactive
114
+ }
115
+ ) }),
116
+ /* @__PURE__ */ jsx4("td", { className: "p-4 text-sm text-muted-foreground", children: tenant.owner_email }),
117
+ /* @__PURE__ */ jsx4("td", { className: "p-4 text-sm text-muted-foreground", children: formatDate(tenant.created_at) }),
118
+ /* @__PURE__ */ jsx4("td", { className: "p-4 text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-1", onClick: (e) => e.stopPropagation(), children: [
119
+ /* @__PURE__ */ jsx4("button", { className: "inline-flex items-center justify-center rounded-md text-sm h-8 w-8 hover:bg-accent", onClick: () => onEdit(tenant), children: /* @__PURE__ */ jsx4(Pencil, { className: "w-4 h-4" }) }),
120
+ /* @__PURE__ */ jsx4("button", { className: "inline-flex items-center justify-center rounded-md text-sm h-8 w-8 hover:bg-accent text-destructive", onClick: () => onDelete(tenant), children: /* @__PURE__ */ jsx4(Trash2, { className: "w-4 h-4" }) })
121
+ ] }) })
122
+ ] }, tenant.id)) })
123
+ ] });
124
+ }
125
+
126
+ // src/components/tenants/TenantFormDialog.tsx
127
+ import { useState, useEffect } from "react";
128
+ import { Loader2 } from "lucide-react";
129
+ import { validateRequired, validateSlug, validateEmail } from "@growflowstudio/growflowbooking-admin-core";
130
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
131
+ function TenantFormDialog({ isOpen, onOpenChange, tenant, onSubmit, isPending }) {
132
+ const isEdit = !!tenant;
133
+ const [name, setName] = useState("");
134
+ const [slug, setSlug] = useState("");
135
+ const [ownerEmail, setOwnerEmail] = useState("");
136
+ const [plan, setPlan] = useState("free");
137
+ const [domain, setDomain] = useState("");
138
+ const [errors, setErrors] = useState({});
139
+ useEffect(() => {
140
+ if (isOpen) {
141
+ if (tenant) {
142
+ setName(tenant.name);
143
+ setSlug(tenant.slug);
144
+ setOwnerEmail(tenant.owner_email);
145
+ setPlan(tenant.plan);
146
+ setDomain(tenant.domain || "");
147
+ } else {
148
+ setName("");
149
+ setSlug("");
150
+ setOwnerEmail("");
151
+ setPlan("free");
152
+ setDomain("");
153
+ }
154
+ setErrors({});
155
+ }
156
+ }, [isOpen, tenant]);
157
+ const handleSubmit = () => {
158
+ const errs = {};
159
+ const nameErr = validateRequired(name, "Il nome");
160
+ if (nameErr) errs.name = nameErr;
161
+ if (!isEdit) {
162
+ const slugErr = validateSlug(slug);
163
+ if (slugErr) errs.slug = slugErr;
164
+ }
165
+ const emailErr = validateEmail(ownerEmail);
166
+ if (emailErr) errs.ownerEmail = emailErr;
167
+ setErrors(errs);
168
+ if (Object.keys(errs).length > 0) return;
169
+ if (isEdit) {
170
+ onSubmit({ name, owner_email: ownerEmail, domain: domain || void 0 });
171
+ } else {
172
+ onSubmit({ name, slug, owner_email: ownerEmail, plan, domain: domain || void 0 });
173
+ }
174
+ };
175
+ if (!isOpen) return null;
176
+ return /* @__PURE__ */ jsxs2("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
177
+ /* @__PURE__ */ jsx5("div", { className: "fixed inset-0 bg-black/80", onClick: () => onOpenChange(false) }),
178
+ /* @__PURE__ */ jsxs2("div", { className: "relative z-50 w-full max-w-lg max-h-[90vh] overflow-y-auto rounded-lg border bg-background p-6 shadow-lg", children: [
179
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-col space-y-1.5 pb-4", children: [
180
+ /* @__PURE__ */ jsx5("h2", { className: "text-lg font-semibold", children: isEdit ? "Modifica Tenant" : "Nuovo Tenant" }),
181
+ /* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground", children: isEdit ? "Modifica i dati del tenant selezionato" : "Crea un nuovo tenant per la piattaforma di booking" })
182
+ ] }),
183
+ /* @__PURE__ */ jsxs2("div", { className: "grid gap-4 py-4", children: [
184
+ /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
185
+ /* @__PURE__ */ jsx5("label", { className: "text-sm font-medium leading-none", children: "Nome *" }),
186
+ /* @__PURE__ */ jsx5("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: name, onChange: (e) => setName(e.target.value), placeholder: "Acme Salon" }),
187
+ errors.name && /* @__PURE__ */ jsx5("p", { className: "text-xs text-destructive", children: errors.name })
188
+ ] }),
189
+ !isEdit && /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
190
+ /* @__PURE__ */ jsx5("label", { className: "text-sm font-medium leading-none", children: "Slug *" }),
191
+ /* @__PURE__ */ jsx5("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: slug, onChange: (e) => setSlug(e.target.value), placeholder: "acme-salon" }),
192
+ errors.slug && /* @__PURE__ */ jsx5("p", { className: "text-xs text-destructive", children: errors.slug })
193
+ ] }),
194
+ /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
195
+ /* @__PURE__ */ jsx5("label", { className: "text-sm font-medium leading-none", children: "Email Proprietario *" }),
196
+ /* @__PURE__ */ jsx5("input", { type: "email", 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: ownerEmail, onChange: (e) => setOwnerEmail(e.target.value), placeholder: "owner@example.com" }),
197
+ errors.ownerEmail && /* @__PURE__ */ jsx5("p", { className: "text-xs text-destructive", children: errors.ownerEmail })
198
+ ] }),
199
+ !isEdit && /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
200
+ /* @__PURE__ */ jsx5("label", { className: "text-sm font-medium leading-none", children: "Piano" }),
201
+ /* @__PURE__ */ jsxs2("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: plan, onChange: (e) => setPlan(e.target.value), children: [
202
+ /* @__PURE__ */ jsx5("option", { value: "free", children: "Free" }),
203
+ /* @__PURE__ */ jsx5("option", { value: "starter", children: "Starter" }),
204
+ /* @__PURE__ */ jsx5("option", { value: "professional", children: "Professional" }),
205
+ /* @__PURE__ */ jsx5("option", { value: "enterprise", children: "Enterprise" })
206
+ ] })
207
+ ] }),
208
+ /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
209
+ /* @__PURE__ */ jsx5("label", { className: "text-sm font-medium leading-none", children: "Dominio" }),
210
+ /* @__PURE__ */ jsx5("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: domain, onChange: (e) => setDomain(e.target.value), placeholder: "salon.example.com" })
211
+ ] })
212
+ ] }),
213
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-4", children: [
214
+ /* @__PURE__ */ jsx5("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" }),
215
+ /* @__PURE__ */ jsxs2("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: isPending, children: [
216
+ isPending && /* @__PURE__ */ jsx5(Loader2, { className: "w-4 h-4 animate-spin mr-2" }),
217
+ isEdit ? "Salva Modifiche" : "Crea Tenant"
218
+ ] })
219
+ ] })
220
+ ] })
221
+ ] });
222
+ }
223
+
224
+ // src/components/tenants/TenantDetailDialog.tsx
225
+ import { X } from "lucide-react";
226
+ import { tenantPlanColors as tenantPlanColors2, tenantPlanLabels as tenantPlanLabels2, tenantStatusColors as tenantStatusColors2, tenantStatusLabels as tenantStatusLabels2, formatDate as formatDate2 } from "@growflowstudio/growflowbooking-admin-core";
227
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
228
+ function TenantDetailDialog({ isOpen, onOpenChange, tenant, isLoading }) {
229
+ if (!isOpen) return null;
230
+ return /* @__PURE__ */ jsxs3("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
231
+ /* @__PURE__ */ jsx6("div", { className: "fixed inset-0 bg-black/80", onClick: () => onOpenChange(false) }),
232
+ /* @__PURE__ */ jsxs3("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: [
233
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between pb-4", children: [
234
+ /* @__PURE__ */ jsx6("h2", { className: "text-lg font-semibold", children: "Dettaglio Tenant" }),
235
+ /* @__PURE__ */ jsx6("button", { className: "rounded-md h-8 w-8 inline-flex items-center justify-center hover:bg-accent", onClick: () => onOpenChange(false), children: /* @__PURE__ */ jsx6(X, { className: "w-4 h-4" }) })
236
+ ] }),
237
+ isLoading || !tenant ? /* @__PURE__ */ jsx6("div", { className: "space-y-4", children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx6("div", { className: "h-6 w-full animate-pulse rounded-md bg-muted" }, i)) }) : /* @__PURE__ */ jsxs3("div", { className: "space-y-6", children: [
238
+ /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-2 gap-4", children: [
239
+ /* @__PURE__ */ jsxs3("div", { children: [
240
+ /* @__PURE__ */ jsx6("span", { className: "text-sm text-muted-foreground", children: "Nome" }),
241
+ /* @__PURE__ */ jsx6("p", { className: "font-medium", children: tenant.name })
242
+ ] }),
243
+ /* @__PURE__ */ jsxs3("div", { children: [
244
+ /* @__PURE__ */ jsx6("span", { className: "text-sm text-muted-foreground", children: "Slug" }),
245
+ /* @__PURE__ */ jsx6("p", { children: /* @__PURE__ */ jsx6("code", { className: "text-xs bg-muted px-1.5 py-0.5 rounded", children: tenant.slug }) })
246
+ ] }),
247
+ /* @__PURE__ */ jsxs3("div", { children: [
248
+ /* @__PURE__ */ jsx6("span", { className: "text-sm text-muted-foreground", children: "Piano" }),
249
+ /* @__PURE__ */ jsx6("p", { children: /* @__PURE__ */ jsx6(StatusBadge, { label: tenantPlanLabels2[tenant.plan], colorClass: tenantPlanColors2[tenant.plan] }) })
250
+ ] }),
251
+ /* @__PURE__ */ jsxs3("div", { children: [
252
+ /* @__PURE__ */ jsx6("span", { className: "text-sm text-muted-foreground", children: "Stato" }),
253
+ /* @__PURE__ */ jsx6("p", { children: /* @__PURE__ */ jsx6(StatusBadge, { label: tenant.is_active ? tenantStatusLabels2.active : tenantStatusLabels2.inactive, colorClass: tenant.is_active ? tenantStatusColors2.active : tenantStatusColors2.inactive }) })
254
+ ] }),
255
+ /* @__PURE__ */ jsxs3("div", { children: [
256
+ /* @__PURE__ */ jsx6("span", { className: "text-sm text-muted-foreground", children: "Email" }),
257
+ /* @__PURE__ */ jsx6("p", { className: "text-sm", children: tenant.owner_email })
258
+ ] }),
259
+ /* @__PURE__ */ jsxs3("div", { children: [
260
+ /* @__PURE__ */ jsx6("span", { className: "text-sm text-muted-foreground", children: "Dominio" }),
261
+ /* @__PURE__ */ jsx6("p", { className: "text-sm", children: tenant.domain || "-" })
262
+ ] }),
263
+ /* @__PURE__ */ jsxs3("div", { children: [
264
+ /* @__PURE__ */ jsx6("span", { className: "text-sm text-muted-foreground", children: "Creato" }),
265
+ /* @__PURE__ */ jsx6("p", { className: "text-sm", children: formatDate2(tenant.created_at) })
266
+ ] }),
267
+ /* @__PURE__ */ jsxs3("div", { children: [
268
+ /* @__PURE__ */ jsx6("span", { className: "text-sm text-muted-foreground", children: "Aggiornato" }),
269
+ /* @__PURE__ */ jsx6("p", { className: "text-sm", children: formatDate2(tenant.updated_at) })
270
+ ] })
271
+ ] }),
272
+ /* @__PURE__ */ jsxs3("div", { className: "border-t pt-4", children: [
273
+ /* @__PURE__ */ jsx6("h3", { className: "font-medium mb-3", children: "Statistiche" }),
274
+ /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-3 gap-4", children: [
275
+ /* @__PURE__ */ jsxs3("div", { className: "rounded-lg border p-3 text-center", children: [
276
+ /* @__PURE__ */ jsx6("p", { className: "text-2xl font-bold", children: tenant.services_count }),
277
+ /* @__PURE__ */ jsx6("p", { className: "text-xs text-muted-foreground", children: "Servizi" })
278
+ ] }),
279
+ /* @__PURE__ */ jsxs3("div", { className: "rounded-lg border p-3 text-center", children: [
280
+ /* @__PURE__ */ jsx6("p", { className: "text-2xl font-bold", children: tenant.staff_count }),
281
+ /* @__PURE__ */ jsx6("p", { className: "text-xs text-muted-foreground", children: "Staff" })
282
+ ] }),
283
+ /* @__PURE__ */ jsxs3("div", { className: "rounded-lg border p-3 text-center", children: [
284
+ /* @__PURE__ */ jsx6("p", { className: "text-2xl font-bold", children: tenant.locations_count }),
285
+ /* @__PURE__ */ jsx6("p", { className: "text-xs text-muted-foreground", children: "Sedi" })
286
+ ] }),
287
+ /* @__PURE__ */ jsxs3("div", { className: "rounded-lg border p-3 text-center", children: [
288
+ /* @__PURE__ */ jsx6("p", { className: "text-2xl font-bold", children: tenant.customers_count }),
289
+ /* @__PURE__ */ jsx6("p", { className: "text-xs text-muted-foreground", children: "Clienti" })
290
+ ] }),
291
+ /* @__PURE__ */ jsxs3("div", { className: "rounded-lg border p-3 text-center", children: [
292
+ /* @__PURE__ */ jsx6("p", { className: "text-2xl font-bold", children: tenant.bookings_count }),
293
+ /* @__PURE__ */ jsx6("p", { className: "text-xs text-muted-foreground", children: "Prenotazioni" })
294
+ ] })
295
+ ] })
296
+ ] }),
297
+ /* @__PURE__ */ jsxs3("div", { className: "border-t pt-4", children: [
298
+ /* @__PURE__ */ jsx6("h3", { className: "font-medium mb-3", children: "Limiti" }),
299
+ /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-3 gap-4 text-sm", children: [
300
+ /* @__PURE__ */ jsxs3("div", { children: [
301
+ /* @__PURE__ */ jsx6("span", { className: "text-muted-foreground", children: "Max Servizi:" }),
302
+ " ",
303
+ tenant.max_services === -1 ? "Illimitati" : tenant.max_services
304
+ ] }),
305
+ /* @__PURE__ */ jsxs3("div", { children: [
306
+ /* @__PURE__ */ jsx6("span", { className: "text-muted-foreground", children: "Max Staff:" }),
307
+ " ",
308
+ tenant.max_staff === -1 ? "Illimitati" : tenant.max_staff
309
+ ] }),
310
+ /* @__PURE__ */ jsxs3("div", { children: [
311
+ /* @__PURE__ */ jsx6("span", { className: "text-muted-foreground", children: "Max Sedi:" }),
312
+ " ",
313
+ tenant.max_locations === -1 ? "Illimitate" : tenant.max_locations
314
+ ] })
315
+ ] })
316
+ ] })
317
+ ] })
318
+ ] })
319
+ ] });
320
+ }
321
+
322
+ // src/components/shared/DeleteConfirmDialog.tsx
323
+ import { Loader2 as Loader22 } from "lucide-react";
324
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
325
+ function DeleteConfirmDialog({
326
+ isOpen,
327
+ onOpenChange,
328
+ title = "Sei sicuro?",
329
+ description,
330
+ onConfirm,
331
+ isDeleting,
332
+ confirmLabel = "Elimina",
333
+ cancelLabel = "Annulla"
334
+ }) {
335
+ if (!isOpen) return null;
336
+ return /* @__PURE__ */ jsxs4("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
337
+ /* @__PURE__ */ jsx7(
338
+ "div",
339
+ {
340
+ className: "fixed inset-0 bg-black/80",
341
+ onClick: () => onOpenChange(false)
342
+ }
343
+ ),
344
+ /* @__PURE__ */ jsxs4("div", { className: "relative z-50 w-full max-w-lg rounded-lg border bg-background p-6 shadow-lg", children: [
345
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-col space-y-2 text-center sm:text-left", children: [
346
+ /* @__PURE__ */ jsx7("h2", { className: "text-lg font-semibold", children: title }),
347
+ /* @__PURE__ */ jsx7("div", { className: "text-sm text-muted-foreground", children: description })
348
+ ] }),
349
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 mt-4", children: [
350
+ /* @__PURE__ */ jsx7(
351
+ "button",
352
+ {
353
+ 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",
354
+ onClick: () => onOpenChange(false),
355
+ children: cancelLabel
356
+ }
357
+ ),
358
+ /* @__PURE__ */ jsxs4(
359
+ "button",
360
+ {
361
+ 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",
362
+ onClick: onConfirm,
363
+ disabled: isDeleting,
364
+ children: [
365
+ isDeleting && /* @__PURE__ */ jsx7(Loader22, { className: "w-4 h-4 animate-spin mr-2" }),
366
+ confirmLabel
367
+ ]
368
+ }
369
+ )
370
+ ] })
371
+ ] })
372
+ ] });
373
+ }
374
+
375
+ // src/components/shared/FilterBar.tsx
376
+ import { Search, RefreshCw } from "lucide-react";
377
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
378
+ function FilterBar({
379
+ searchPlaceholder = "Cerca...",
380
+ searchValue,
381
+ onSearchChange,
382
+ statusFilter,
383
+ onStatusFilterChange,
384
+ statusOptions,
385
+ onRefresh
386
+ }) {
387
+ return /* @__PURE__ */ jsx8("div", { className: "rounded-lg border bg-card text-card-foreground shadow-sm", children: /* @__PURE__ */ jsx8("div", { className: "p-6 pt-6", children: /* @__PURE__ */ jsxs5("div", { className: "flex flex-col sm:flex-row gap-4", children: [
388
+ /* @__PURE__ */ jsxs5("div", { className: "relative flex-1", children: [
389
+ /* @__PURE__ */ jsx8(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" }),
390
+ /* @__PURE__ */ jsx8(
391
+ "input",
392
+ {
393
+ 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",
394
+ placeholder: searchPlaceholder,
395
+ value: searchValue,
396
+ onChange: (e) => onSearchChange(e.target.value)
397
+ }
398
+ )
399
+ ] }),
400
+ statusOptions && onStatusFilterChange && /* @__PURE__ */ jsx8(
401
+ "select",
402
+ {
403
+ 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",
404
+ value: statusFilter || "all",
405
+ onChange: (e) => onStatusFilterChange(e.target.value),
406
+ children: statusOptions.map((opt) => /* @__PURE__ */ jsx8("option", { value: opt.value, children: opt.label }, opt.value))
407
+ }
408
+ ),
409
+ /* @__PURE__ */ jsxs5(
410
+ "button",
411
+ {
412
+ 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",
413
+ onClick: onRefresh,
414
+ children: [
415
+ /* @__PURE__ */ jsx8(RefreshCw, { className: "w-4 h-4 mr-2" }),
416
+ "Aggiorna"
417
+ ]
418
+ }
419
+ )
420
+ ] }) }) });
421
+ }
422
+
423
+ // src/components/shared/Pagination.tsx
424
+ import { ChevronLeft, ChevronRight } from "lucide-react";
425
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
426
+ function Pagination({ page, pageSize, total, onPageChange }) {
427
+ const totalPages = Math.ceil(total / pageSize);
428
+ return /* @__PURE__ */ jsxs6("div", { className: "flex justify-between items-center", children: [
429
+ /* @__PURE__ */ jsx9("span", { className: "text-sm text-muted-foreground", children: total > 0 ? `${(page - 1) * pageSize + 1}-${Math.min(page * pageSize, total)} di ${total}` : "Nessun risultato" }),
430
+ /* @__PURE__ */ jsxs6("div", { className: "flex gap-2", children: [
431
+ /* @__PURE__ */ jsxs6(
432
+ "button",
433
+ {
434
+ 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",
435
+ onClick: () => onPageChange(page - 1),
436
+ disabled: page <= 1,
437
+ children: [
438
+ /* @__PURE__ */ jsx9(ChevronLeft, { className: "w-4 h-4 mr-1" }),
439
+ "Precedente"
440
+ ]
441
+ }
442
+ ),
443
+ /* @__PURE__ */ jsxs6(
444
+ "button",
445
+ {
446
+ 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",
447
+ onClick: () => onPageChange(page + 1),
448
+ disabled: page >= totalPages,
449
+ children: [
450
+ "Successiva",
451
+ /* @__PURE__ */ jsx9(ChevronRight, { className: "w-4 h-4 ml-1" })
452
+ ]
453
+ }
454
+ )
455
+ ] })
456
+ ] });
457
+ }
458
+
459
+ // src/pages/TenantsPage.tsx
460
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
461
+ function TenantsPage({ wrapper: Wrapper, header }) {
462
+ const [search, setSearch] = useState2("");
463
+ const [planFilter, setPlanFilter] = useState2("all");
464
+ const [page, setPage] = useState2(1);
465
+ const pageSize = 20;
466
+ const [showForm, setShowForm] = useState2(false);
467
+ const [showDelete, setShowDelete] = useState2(false);
468
+ const [showDetail, setShowDetail] = useState2(false);
469
+ const [selectedTenant, setSelectedTenant] = useState2(null);
470
+ const { data, isLoading, refetch } = useAdminTenants({
471
+ search: search || void 0,
472
+ plan: planFilter !== "all" ? planFilter : void 0,
473
+ page,
474
+ page_size: pageSize
475
+ });
476
+ const { data: tenantDetail, isLoading: isLoadingDetail } = useAdminTenant(
477
+ showDetail && selectedTenant ? selectedTenant.id : ""
478
+ );
479
+ const createMutation = useCreateTenant();
480
+ const updateMutation = useUpdateTenant();
481
+ const deleteMutation = useDeleteTenant();
482
+ const tenants = data?.items || [];
483
+ const total = data?.total || 0;
484
+ const handleSubmit = (formData) => {
485
+ if (selectedTenant) {
486
+ updateMutation.mutate({ id: selectedTenant.id, data: formData }, {
487
+ onSuccess: () => {
488
+ toast.success("Tenant aggiornato con successo");
489
+ setShowForm(false);
490
+ setSelectedTenant(null);
491
+ },
492
+ onError: (err) => toast.error(`Errore: ${err.message}`)
493
+ });
494
+ } else {
495
+ createMutation.mutate(formData, {
496
+ onSuccess: () => {
497
+ toast.success("Tenant creato con successo");
498
+ setShowForm(false);
499
+ },
500
+ onError: (err) => toast.error(`Errore: ${err.message}`)
501
+ });
502
+ }
503
+ };
504
+ const handleDelete = () => {
505
+ if (selectedTenant) {
506
+ deleteMutation.mutate(selectedTenant.id, {
507
+ onSuccess: () => {
508
+ toast.success("Tenant eliminato con successo");
509
+ setShowDelete(false);
510
+ setSelectedTenant(null);
511
+ },
512
+ onError: (err) => toast.error(`Errore: ${err.message}`)
513
+ });
514
+ }
515
+ };
516
+ const content = /* @__PURE__ */ jsxs7(Fragment2, { children: [
517
+ header,
518
+ /* @__PURE__ */ jsxs7("div", { className: "space-y-4", children: [
519
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between", children: [
520
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
521
+ /* @__PURE__ */ jsx10(Building2, { className: "w-4 h-4" }),
522
+ /* @__PURE__ */ jsxs7("span", { children: [
523
+ total,
524
+ " tenant"
525
+ ] })
526
+ ] }),
527
+ /* @__PURE__ */ jsxs7("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: () => {
528
+ setSelectedTenant(null);
529
+ setShowForm(true);
530
+ }, children: [
531
+ /* @__PURE__ */ jsx10(Plus, { className: "w-4 h-4 mr-2" }),
532
+ "Nuovo Tenant"
533
+ ] })
534
+ ] }),
535
+ /* @__PURE__ */ jsx10(
536
+ FilterBar,
537
+ {
538
+ searchPlaceholder: "Cerca tenant...",
539
+ searchValue: search,
540
+ onSearchChange: (v) => {
541
+ setSearch(v);
542
+ setPage(1);
543
+ },
544
+ statusFilter: planFilter,
545
+ onStatusFilterChange: (v) => {
546
+ setPlanFilter(v);
547
+ setPage(1);
548
+ },
549
+ statusOptions: [
550
+ { value: "all", label: "Tutti i piani" },
551
+ { value: "free", label: "Free" },
552
+ { value: "starter", label: "Starter" },
553
+ { value: "professional", label: "Professional" },
554
+ { value: "enterprise", label: "Enterprise" }
555
+ ],
556
+ onRefresh: () => refetch()
557
+ }
558
+ ),
559
+ /* @__PURE__ */ jsxs7("div", { className: "rounded-lg border bg-card text-card-foreground shadow-sm", children: [
560
+ /* @__PURE__ */ jsx10("div", { className: "p-0", children: /* @__PURE__ */ jsx10(
561
+ TenantsTable,
562
+ {
563
+ tenants,
564
+ isLoading,
565
+ onEdit: (t) => {
566
+ setSelectedTenant(t);
567
+ setShowForm(true);
568
+ },
569
+ onDelete: (t) => {
570
+ setSelectedTenant(t);
571
+ setShowDelete(true);
572
+ },
573
+ onView: (t) => {
574
+ setSelectedTenant(t);
575
+ setShowDetail(true);
576
+ }
577
+ }
578
+ ) }),
579
+ total > pageSize && /* @__PURE__ */ jsx10("div", { className: "p-4 border-t", children: /* @__PURE__ */ jsx10(Pagination, { page, pageSize, total, onPageChange: setPage }) })
580
+ ] })
581
+ ] }),
582
+ /* @__PURE__ */ jsx10(
583
+ TenantFormDialog,
584
+ {
585
+ isOpen: showForm,
586
+ onOpenChange: setShowForm,
587
+ tenant: selectedTenant,
588
+ onSubmit: handleSubmit,
589
+ isPending: createMutation.isPending || updateMutation.isPending
590
+ }
591
+ ),
592
+ /* @__PURE__ */ jsx10(
593
+ TenantDetailDialog,
594
+ {
595
+ isOpen: showDetail,
596
+ onOpenChange: setShowDetail,
597
+ tenant: tenantDetail,
598
+ isLoading: isLoadingDetail
599
+ }
600
+ ),
601
+ /* @__PURE__ */ jsx10(
602
+ DeleteConfirmDialog,
603
+ {
604
+ isOpen: showDelete,
605
+ onOpenChange: setShowDelete,
606
+ title: "Eliminare Tenant?",
607
+ description: /* @__PURE__ */ jsxs7(Fragment2, { children: [
608
+ "Stai per eliminare il tenant ",
609
+ /* @__PURE__ */ jsx10("strong", { children: selectedTenant?.name }),
610
+ ". Questa azione non pu\xF2 essere annullata e rimuover\xE0 tutti i dati associati."
611
+ ] }),
612
+ onConfirm: handleDelete,
613
+ isDeleting: deleteMutation.isPending,
614
+ confirmLabel: "Elimina Tenant"
615
+ }
616
+ )
617
+ ] });
618
+ return Wrapper ? /* @__PURE__ */ jsx10(Wrapper, { children: content }) : content;
619
+ }
620
+
621
+ // src/pages/CustomersPage.tsx
622
+ import { useState as useState4 } from "react";
623
+ import { toast as toast2 } from "sonner";
624
+ import { Plus as Plus2, Users } from "lucide-react";
625
+ import {
626
+ useAdminCustomers,
627
+ useCreateCustomer,
628
+ useUpdateCustomer,
629
+ useDeleteCustomer
630
+ } from "@growflowstudio/growflowbooking-admin-core";
631
+
632
+ // src/components/customers/CustomersTable.tsx
633
+ import { Pencil as Pencil2, Trash2 as Trash22 } from "lucide-react";
634
+ import { formatDate as formatDate3 } from "@growflowstudio/growflowbooking-admin-core";
635
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
636
+ function CustomersTable({ customers, isLoading, onEdit, onDelete }) {
637
+ return /* @__PURE__ */ jsxs8("table", { className: "w-full", children: [
638
+ /* @__PURE__ */ jsx11("thead", { children: /* @__PURE__ */ jsxs8("tr", { className: "border-b", children: [
639
+ /* @__PURE__ */ jsx11("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Nome" }),
640
+ /* @__PURE__ */ jsx11("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Email" }),
641
+ /* @__PURE__ */ jsx11("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Tenant" }),
642
+ /* @__PURE__ */ jsx11("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Stato" }),
643
+ /* @__PURE__ */ jsx11("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Creato" }),
644
+ /* @__PURE__ */ jsx11("th", { className: "h-12 px-4 text-right align-middle font-medium text-muted-foreground", children: "Azioni" })
645
+ ] }) }),
646
+ /* @__PURE__ */ jsx11("tbody", { children: isLoading ? /* @__PURE__ */ jsx11(SkeletonRows, { rows: 5, columns: 6 }) : customers.length === 0 ? /* @__PURE__ */ jsx11("tr", { children: /* @__PURE__ */ jsx11("td", { colSpan: 6, className: "h-24 text-center text-muted-foreground", children: "Nessun cliente trovato." }) }) : customers.map((customer) => /* @__PURE__ */ jsxs8("tr", { className: "border-b hover:bg-muted/50", children: [
647
+ /* @__PURE__ */ jsxs8("td", { className: "p-4 font-medium", children: [
648
+ customer.first_name,
649
+ " ",
650
+ customer.last_name
651
+ ] }),
652
+ /* @__PURE__ */ jsx11("td", { className: "p-4 text-sm", children: customer.email }),
653
+ /* @__PURE__ */ jsx11("td", { className: "p-4 text-sm text-muted-foreground", children: customer.tenant_name }),
654
+ /* @__PURE__ */ jsx11("td", { className: "p-4", children: /* @__PURE__ */ jsx11(
655
+ StatusBadge,
656
+ {
657
+ label: customer.is_active ? "Attivo" : "Inattivo",
658
+ colorClass: customer.is_active ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-400"
659
+ }
660
+ ) }),
661
+ /* @__PURE__ */ jsx11("td", { className: "p-4 text-sm text-muted-foreground", children: formatDate3(customer.created_at) }),
662
+ /* @__PURE__ */ jsx11("td", { className: "p-4 text-right", children: /* @__PURE__ */ jsxs8("div", { className: "flex justify-end gap-1", children: [
663
+ /* @__PURE__ */ jsx11("button", { className: "inline-flex items-center justify-center rounded-md text-sm h-8 w-8 hover:bg-accent", onClick: () => onEdit(customer), children: /* @__PURE__ */ jsx11(Pencil2, { className: "w-4 h-4" }) }),
664
+ /* @__PURE__ */ jsx11("button", { className: "inline-flex items-center justify-center rounded-md text-sm h-8 w-8 hover:bg-accent text-destructive", onClick: () => onDelete(customer), children: /* @__PURE__ */ jsx11(Trash22, { className: "w-4 h-4" }) })
665
+ ] }) })
666
+ ] }, customer.id)) })
667
+ ] });
668
+ }
669
+
670
+ // src/components/customers/CustomerFormDialog.tsx
671
+ import { useState as useState3, useEffect as useEffect2 } from "react";
672
+ import { Loader2 as Loader23 } from "lucide-react";
673
+ import { validateRequired as validateRequired2, validateEmail as validateEmail2 } from "@growflowstudio/growflowbooking-admin-core";
674
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
675
+ function CustomerFormDialog({ isOpen, onOpenChange, customer, onSubmit, isPending, tenantId }) {
676
+ const isEdit = !!customer;
677
+ const [firstName, setFirstName] = useState3("");
678
+ const [lastName, setLastName] = useState3("");
679
+ const [email, setEmail] = useState3("");
680
+ const [phone, setPhone] = useState3("");
681
+ const [notes, setNotes] = useState3("");
682
+ const [errors, setErrors] = useState3({});
683
+ useEffect2(() => {
684
+ if (isOpen) {
685
+ if (customer) {
686
+ setFirstName(customer.first_name);
687
+ setLastName(customer.last_name);
688
+ setEmail(customer.email);
689
+ setPhone(customer.phone || "");
690
+ setNotes(customer.notes || "");
691
+ } else {
692
+ setFirstName("");
693
+ setLastName("");
694
+ setEmail("");
695
+ setPhone("");
696
+ setNotes("");
697
+ }
698
+ setErrors({});
699
+ }
700
+ }, [isOpen, customer]);
701
+ const handleSubmit = () => {
702
+ const errs = {};
703
+ const fnErr = validateRequired2(firstName, "Il nome");
704
+ if (fnErr) errs.firstName = fnErr;
705
+ const lnErr = validateRequired2(lastName, "Il cognome");
706
+ if (lnErr) errs.lastName = lnErr;
707
+ const emailErr = validateEmail2(email);
708
+ if (emailErr) errs.email = emailErr;
709
+ setErrors(errs);
710
+ if (Object.keys(errs).length > 0) return;
711
+ if (isEdit) {
712
+ onSubmit({ first_name: firstName, last_name: lastName, email, phone: phone || void 0, notes: notes || void 0 });
713
+ } else {
714
+ onSubmit({ tenant_id: tenantId || "", first_name: firstName, last_name: lastName, email, phone: phone || void 0, notes: notes || void 0 });
715
+ }
716
+ };
717
+ if (!isOpen) return null;
718
+ return /* @__PURE__ */ jsxs9("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
719
+ /* @__PURE__ */ jsx12("div", { className: "fixed inset-0 bg-black/80", onClick: () => onOpenChange(false) }),
720
+ /* @__PURE__ */ jsxs9("div", { className: "relative z-50 w-full max-w-lg max-h-[90vh] overflow-y-auto rounded-lg border bg-background p-6 shadow-lg", children: [
721
+ /* @__PURE__ */ jsx12("div", { className: "flex flex-col space-y-1.5 pb-4", children: /* @__PURE__ */ jsx12("h2", { className: "text-lg font-semibold", children: isEdit ? "Modifica Cliente" : "Nuovo Cliente" }) }),
722
+ /* @__PURE__ */ jsxs9("div", { className: "grid gap-4 py-4", children: [
723
+ /* @__PURE__ */ jsxs9("div", { className: "grid grid-cols-2 gap-4", children: [
724
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
725
+ /* @__PURE__ */ jsx12("label", { className: "text-sm font-medium leading-none", children: "Nome *" }),
726
+ /* @__PURE__ */ jsx12("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: firstName, onChange: (e) => setFirstName(e.target.value), placeholder: "Mario" }),
727
+ errors.firstName && /* @__PURE__ */ jsx12("p", { className: "text-xs text-destructive", children: errors.firstName })
728
+ ] }),
729
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
730
+ /* @__PURE__ */ jsx12("label", { className: "text-sm font-medium leading-none", children: "Cognome *" }),
731
+ /* @__PURE__ */ jsx12("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: lastName, onChange: (e) => setLastName(e.target.value), placeholder: "Rossi" }),
732
+ errors.lastName && /* @__PURE__ */ jsx12("p", { className: "text-xs text-destructive", children: errors.lastName })
733
+ ] })
734
+ ] }),
735
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
736
+ /* @__PURE__ */ jsx12("label", { className: "text-sm font-medium leading-none", children: "Email *" }),
737
+ /* @__PURE__ */ jsx12("input", { type: "email", 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: email, onChange: (e) => setEmail(e.target.value), placeholder: "mario@example.com" }),
738
+ errors.email && /* @__PURE__ */ jsx12("p", { className: "text-xs text-destructive", children: errors.email })
739
+ ] }),
740
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
741
+ /* @__PURE__ */ jsx12("label", { className: "text-sm font-medium leading-none", children: "Telefono" }),
742
+ /* @__PURE__ */ jsx12("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: phone, onChange: (e) => setPhone(e.target.value), placeholder: "+39 123 456 7890" })
743
+ ] }),
744
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
745
+ /* @__PURE__ */ jsx12("label", { className: "text-sm font-medium leading-none", children: "Note" }),
746
+ /* @__PURE__ */ jsx12("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: notes, onChange: (e) => setNotes(e.target.value), rows: 2 })
747
+ ] })
748
+ ] }),
749
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-4", children: [
750
+ /* @__PURE__ */ jsx12("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" }),
751
+ /* @__PURE__ */ jsxs9("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: isPending, children: [
752
+ isPending && /* @__PURE__ */ jsx12(Loader23, { className: "w-4 h-4 animate-spin mr-2" }),
753
+ isEdit ? "Salva Modifiche" : "Crea Cliente"
754
+ ] })
755
+ ] })
756
+ ] })
757
+ ] });
758
+ }
759
+
760
+ // src/pages/CustomersPage.tsx
761
+ import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
762
+ function CustomersPage({ wrapper: Wrapper, header }) {
763
+ const [search, setSearch] = useState4("");
764
+ const [page, setPage] = useState4(1);
765
+ const pageSize = 20;
766
+ const [showForm, setShowForm] = useState4(false);
767
+ const [showDelete, setShowDelete] = useState4(false);
768
+ const [selectedCustomer, setSelectedCustomer] = useState4(null);
769
+ const { data, isLoading, refetch } = useAdminCustomers({
770
+ search: search || void 0,
771
+ page,
772
+ page_size: pageSize
773
+ });
774
+ const createMutation = useCreateCustomer();
775
+ const updateMutation = useUpdateCustomer();
776
+ const deleteMutation = useDeleteCustomer();
777
+ const customers = data?.items || [];
778
+ const total = data?.total || 0;
779
+ const handleSubmit = (formData) => {
780
+ if (selectedCustomer) {
781
+ updateMutation.mutate({ id: selectedCustomer.id, data: formData }, {
782
+ onSuccess: () => {
783
+ toast2.success("Cliente aggiornato con successo");
784
+ setShowForm(false);
785
+ setSelectedCustomer(null);
786
+ },
787
+ onError: (err) => toast2.error(`Errore: ${err.message}`)
788
+ });
789
+ } else {
790
+ createMutation.mutate(formData, {
791
+ onSuccess: () => {
792
+ toast2.success("Cliente creato con successo");
793
+ setShowForm(false);
794
+ },
795
+ onError: (err) => toast2.error(`Errore: ${err.message}`)
796
+ });
797
+ }
798
+ };
799
+ const handleDelete = () => {
800
+ if (selectedCustomer) {
801
+ deleteMutation.mutate(selectedCustomer.id, {
802
+ onSuccess: () => {
803
+ toast2.success("Cliente eliminato con successo");
804
+ setShowDelete(false);
805
+ setSelectedCustomer(null);
806
+ },
807
+ onError: (err) => toast2.error(`Errore: ${err.message}`)
808
+ });
809
+ }
810
+ };
811
+ const content = /* @__PURE__ */ jsxs10(Fragment3, { children: [
812
+ header,
813
+ /* @__PURE__ */ jsxs10("div", { className: "space-y-4", children: [
814
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between", children: [
815
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
816
+ /* @__PURE__ */ jsx13(Users, { className: "w-4 h-4" }),
817
+ /* @__PURE__ */ jsxs10("span", { children: [
818
+ total,
819
+ " clienti"
820
+ ] })
821
+ ] }),
822
+ /* @__PURE__ */ jsxs10("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: () => {
823
+ setSelectedCustomer(null);
824
+ setShowForm(true);
825
+ }, children: [
826
+ /* @__PURE__ */ jsx13(Plus2, { className: "w-4 h-4 mr-2" }),
827
+ "Nuovo Cliente"
828
+ ] })
829
+ ] }),
830
+ /* @__PURE__ */ jsx13(
831
+ FilterBar,
832
+ {
833
+ searchPlaceholder: "Cerca clienti...",
834
+ searchValue: search,
835
+ onSearchChange: (v) => {
836
+ setSearch(v);
837
+ setPage(1);
838
+ },
839
+ onRefresh: () => refetch()
840
+ }
841
+ ),
842
+ /* @__PURE__ */ jsxs10("div", { className: "rounded-lg border bg-card text-card-foreground shadow-sm", children: [
843
+ /* @__PURE__ */ jsx13("div", { className: "p-0", children: /* @__PURE__ */ jsx13(
844
+ CustomersTable,
845
+ {
846
+ customers,
847
+ isLoading,
848
+ onEdit: (c) => {
849
+ setSelectedCustomer(c);
850
+ setShowForm(true);
851
+ },
852
+ onDelete: (c) => {
853
+ setSelectedCustomer(c);
854
+ setShowDelete(true);
855
+ }
856
+ }
857
+ ) }),
858
+ total > pageSize && /* @__PURE__ */ jsx13("div", { className: "p-4 border-t", children: /* @__PURE__ */ jsx13(Pagination, { page, pageSize, total, onPageChange: setPage }) })
859
+ ] })
860
+ ] }),
861
+ /* @__PURE__ */ jsx13(
862
+ CustomerFormDialog,
863
+ {
864
+ isOpen: showForm,
865
+ onOpenChange: setShowForm,
866
+ customer: selectedCustomer,
867
+ onSubmit: handleSubmit,
868
+ isPending: createMutation.isPending || updateMutation.isPending
869
+ }
870
+ ),
871
+ /* @__PURE__ */ jsx13(
872
+ DeleteConfirmDialog,
873
+ {
874
+ isOpen: showDelete,
875
+ onOpenChange: setShowDelete,
876
+ title: "Eliminare Cliente?",
877
+ description: /* @__PURE__ */ jsxs10(Fragment3, { children: [
878
+ "Stai per eliminare il cliente ",
879
+ /* @__PURE__ */ jsxs10("strong", { children: [
880
+ selectedCustomer?.first_name,
881
+ " ",
882
+ selectedCustomer?.last_name
883
+ ] }),
884
+ ". Questa azione non pu\xF2 essere annullata."
885
+ ] }),
886
+ onConfirm: handleDelete,
887
+ isDeleting: deleteMutation.isPending,
888
+ confirmLabel: "Elimina Cliente"
889
+ }
890
+ )
891
+ ] });
892
+ return Wrapper ? /* @__PURE__ */ jsx13(Wrapper, { children: content }) : content;
893
+ }
894
+
895
+ // src/pages/SubscriptionPlansPage.tsx
896
+ import { useState as useState6 } from "react";
897
+ import { toast as toast3 } from "sonner";
898
+ import { Plus as Plus3, LayoutGrid } from "lucide-react";
899
+ import {
900
+ useSubscriptionPlans,
901
+ useCreateSubscriptionPlan,
902
+ useUpdateSubscriptionPlan,
903
+ useDeleteSubscriptionPlan
904
+ } from "@growflowstudio/growflowbooking-admin-core";
905
+
906
+ // src/components/subscription-plans/SubscriptionPlansTable.tsx
907
+ import { Pencil as Pencil3, Trash2 as Trash23 } from "lucide-react";
908
+ import { formatPrice } from "@growflowstudio/growflowbooking-admin-core";
909
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
910
+ function SubscriptionPlansTable({ plans, isLoading, onEdit, onDelete }) {
911
+ return /* @__PURE__ */ jsxs11("table", { className: "w-full", children: [
912
+ /* @__PURE__ */ jsx14("thead", { children: /* @__PURE__ */ jsxs11("tr", { className: "border-b", children: [
913
+ /* @__PURE__ */ jsx14("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Nome" }),
914
+ /* @__PURE__ */ jsx14("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Slug" }),
915
+ /* @__PURE__ */ jsx14("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Prezzo Mensile" }),
916
+ /* @__PURE__ */ jsx14("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Prezzo Annuale" }),
917
+ /* @__PURE__ */ jsx14("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Stato" }),
918
+ /* @__PURE__ */ jsx14("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Trial" }),
919
+ /* @__PURE__ */ jsx14("th", { className: "h-12 px-4 text-right align-middle font-medium text-muted-foreground", children: "Azioni" })
920
+ ] }) }),
921
+ /* @__PURE__ */ jsx14("tbody", { children: isLoading ? /* @__PURE__ */ jsx14(SkeletonRows, { rows: 5, columns: 7 }) : plans.length === 0 ? /* @__PURE__ */ jsx14("tr", { children: /* @__PURE__ */ jsx14("td", { colSpan: 7, className: "h-24 text-center text-muted-foreground", children: "Nessun piano trovato." }) }) : plans.map((plan) => /* @__PURE__ */ jsxs11("tr", { className: "border-b hover:bg-muted/50", children: [
922
+ /* @__PURE__ */ jsx14("td", { className: "p-4 font-medium", children: plan.name }),
923
+ /* @__PURE__ */ jsx14("td", { className: "p-4", children: /* @__PURE__ */ jsx14("code", { className: "text-xs bg-muted px-1.5 py-0.5 rounded", children: plan.slug }) }),
924
+ /* @__PURE__ */ jsx14("td", { className: "p-4 text-sm", children: formatPrice(plan.price_monthly, plan.currency) }),
925
+ /* @__PURE__ */ jsx14("td", { className: "p-4 text-sm", children: formatPrice(plan.price_yearly, plan.currency) }),
926
+ /* @__PURE__ */ jsx14("td", { className: "p-4", children: /* @__PURE__ */ jsx14(
927
+ StatusBadge,
928
+ {
929
+ label: plan.is_active ? "Attivo" : "Inattivo",
930
+ colorClass: plan.is_active ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-400"
931
+ }
932
+ ) }),
933
+ /* @__PURE__ */ jsxs11("td", { className: "p-4 text-sm text-muted-foreground", children: [
934
+ plan.trial_days,
935
+ " giorni"
936
+ ] }),
937
+ /* @__PURE__ */ jsx14("td", { className: "p-4 text-right", children: /* @__PURE__ */ jsxs11("div", { className: "flex justify-end gap-1", children: [
938
+ /* @__PURE__ */ jsx14("button", { className: "inline-flex items-center justify-center rounded-md text-sm h-8 w-8 hover:bg-accent", onClick: () => onEdit(plan), children: /* @__PURE__ */ jsx14(Pencil3, { className: "w-4 h-4" }) }),
939
+ /* @__PURE__ */ jsx14("button", { className: "inline-flex items-center justify-center rounded-md text-sm h-8 w-8 hover:bg-accent text-destructive", onClick: () => onDelete(plan), children: /* @__PURE__ */ jsx14(Trash23, { className: "w-4 h-4" }) })
940
+ ] }) })
941
+ ] }, plan.id)) })
942
+ ] });
943
+ }
944
+
945
+ // src/components/subscription-plans/SubscriptionPlanFormDialog.tsx
946
+ import { useState as useState5, useEffect as useEffect3 } from "react";
947
+ import { Loader2 as Loader24 } from "lucide-react";
948
+ import { validateRequired as validateRequired3, validateSlug as validateSlug2 } from "@growflowstudio/growflowbooking-admin-core";
949
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
950
+ function SubscriptionPlanFormDialog({ isOpen, onOpenChange, plan, onSubmit, isPending }) {
951
+ const isEdit = !!plan;
952
+ const [slug, setSlug] = useState5("");
953
+ const [name, setName] = useState5("");
954
+ const [description, setDescription] = useState5("");
955
+ const [pricingModel, setPricingModel] = useState5("flat");
956
+ const [priceMonthly, setPriceMonthly] = useState5("");
957
+ const [priceYearly, setPriceYearly] = useState5("");
958
+ const [currency, setCurrency] = useState5("eur");
959
+ const [trialDays, setTrialDays] = useState5(14);
960
+ const [isActive, setIsActive] = useState5(true);
961
+ const [errors, setErrors] = useState5({});
962
+ useEffect3(() => {
963
+ if (isOpen) {
964
+ if (plan) {
965
+ setSlug(plan.slug);
966
+ setName(plan.name);
967
+ setDescription(plan.description || "");
968
+ setPricingModel(plan.pricing_model);
969
+ setPriceMonthly(plan.price_monthly?.toString() || "");
970
+ setPriceYearly(plan.price_yearly?.toString() || "");
971
+ setCurrency(plan.currency);
972
+ setTrialDays(plan.trial_days);
973
+ setIsActive(plan.is_active);
974
+ } else {
975
+ setSlug("");
976
+ setName("");
977
+ setDescription("");
978
+ setPricingModel("flat");
979
+ setPriceMonthly("");
980
+ setPriceYearly("");
981
+ setCurrency("eur");
982
+ setTrialDays(14);
983
+ setIsActive(true);
984
+ }
985
+ setErrors({});
986
+ }
987
+ }, [isOpen, plan]);
988
+ const handleSubmit = () => {
989
+ const errs = {};
990
+ if (!isEdit) {
991
+ const slugErr = validateSlug2(slug);
992
+ if (slugErr) errs.slug = slugErr;
993
+ }
994
+ const nameErr = validateRequired3(name, "Il nome");
995
+ if (nameErr) errs.name = nameErr;
996
+ setErrors(errs);
997
+ if (Object.keys(errs).length > 0) return;
998
+ const data = {
999
+ ...isEdit ? {} : { slug },
1000
+ name,
1001
+ description: description || void 0,
1002
+ pricing_model: pricingModel,
1003
+ price_monthly: priceMonthly ? parseFloat(priceMonthly) : void 0,
1004
+ price_yearly: priceYearly ? parseFloat(priceYearly) : void 0,
1005
+ currency,
1006
+ trial_days: trialDays,
1007
+ is_active: isActive
1008
+ };
1009
+ onSubmit(data);
1010
+ };
1011
+ if (!isOpen) return null;
1012
+ return /* @__PURE__ */ jsxs12("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
1013
+ /* @__PURE__ */ jsx15("div", { className: "fixed inset-0 bg-black/80", onClick: () => onOpenChange(false) }),
1014
+ /* @__PURE__ */ jsxs12("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: [
1015
+ /* @__PURE__ */ jsx15("div", { className: "flex flex-col space-y-1.5 pb-4", children: /* @__PURE__ */ jsx15("h2", { className: "text-lg font-semibold", children: isEdit ? "Modifica Piano" : "Nuovo Piano" }) }),
1016
+ /* @__PURE__ */ jsxs12("div", { className: "grid gap-4 py-4", children: [
1017
+ !isEdit && /* @__PURE__ */ jsxs12("div", { className: "space-y-2", children: [
1018
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium leading-none", children: "Slug *" }),
1019
+ /* @__PURE__ */ jsx15("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: slug, onChange: (e) => setSlug(e.target.value), placeholder: "pro-monthly" }),
1020
+ errors.slug && /* @__PURE__ */ jsx15("p", { className: "text-xs text-destructive", children: errors.slug })
1021
+ ] }),
1022
+ /* @__PURE__ */ jsxs12("div", { className: "space-y-2", children: [
1023
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium leading-none", children: "Nome *" }),
1024
+ /* @__PURE__ */ jsx15("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: name, onChange: (e) => setName(e.target.value), placeholder: "Professional" }),
1025
+ errors.name && /* @__PURE__ */ jsx15("p", { className: "text-xs text-destructive", children: errors.name })
1026
+ ] }),
1027
+ /* @__PURE__ */ jsxs12("div", { className: "space-y-2", children: [
1028
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium leading-none", children: "Descrizione" }),
1029
+ /* @__PURE__ */ jsx15("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: description, onChange: (e) => setDescription(e.target.value), rows: 2 })
1030
+ ] }),
1031
+ /* @__PURE__ */ jsxs12("div", { className: "grid grid-cols-2 gap-4", children: [
1032
+ /* @__PURE__ */ jsxs12("div", { className: "space-y-2", children: [
1033
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium leading-none", children: "Prezzo Mensile" }),
1034
+ /* @__PURE__ */ jsx15("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: priceMonthly, onChange: (e) => setPriceMonthly(e.target.value), placeholder: "29.00" })
1035
+ ] }),
1036
+ /* @__PURE__ */ jsxs12("div", { className: "space-y-2", children: [
1037
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium leading-none", children: "Prezzo Annuale" }),
1038
+ /* @__PURE__ */ jsx15("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: priceYearly, onChange: (e) => setPriceYearly(e.target.value), placeholder: "290.00" })
1039
+ ] })
1040
+ ] }),
1041
+ /* @__PURE__ */ jsxs12("div", { className: "grid grid-cols-2 gap-4", children: [
1042
+ /* @__PURE__ */ jsxs12("div", { className: "space-y-2", children: [
1043
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium leading-none", children: "Valuta" }),
1044
+ /* @__PURE__ */ jsxs12("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: currency, onChange: (e) => setCurrency(e.target.value), children: [
1045
+ /* @__PURE__ */ jsx15("option", { value: "eur", children: "EUR" }),
1046
+ /* @__PURE__ */ jsx15("option", { value: "usd", children: "USD" }),
1047
+ /* @__PURE__ */ jsx15("option", { value: "gbp", children: "GBP" })
1048
+ ] })
1049
+ ] }),
1050
+ /* @__PURE__ */ jsxs12("div", { className: "space-y-2", children: [
1051
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium leading-none", children: "Giorni Prova" }),
1052
+ /* @__PURE__ */ jsx15("input", { 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: trialDays, onChange: (e) => setTrialDays(parseInt(e.target.value) || 0) })
1053
+ ] })
1054
+ ] }),
1055
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
1056
+ /* @__PURE__ */ jsx15(
1057
+ "button",
1058
+ {
1059
+ type: "button",
1060
+ role: "switch",
1061
+ "aria-checked": isActive,
1062
+ onClick: () => setIsActive(!isActive),
1063
+ className: `inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors ${isActive ? "bg-primary" : "bg-input"}`,
1064
+ children: /* @__PURE__ */ jsx15("span", { className: `pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg transition-transform ${isActive ? "translate-x-4" : "translate-x-0"}` })
1065
+ }
1066
+ ),
1067
+ /* @__PURE__ */ jsx15("span", { className: "text-sm font-medium leading-none", children: "Piano Attivo" })
1068
+ ] })
1069
+ ] }),
1070
+ /* @__PURE__ */ jsxs12("div", { className: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-4", children: [
1071
+ /* @__PURE__ */ jsx15("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" }),
1072
+ /* @__PURE__ */ jsxs12("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: isPending, children: [
1073
+ isPending && /* @__PURE__ */ jsx15(Loader24, { className: "w-4 h-4 animate-spin mr-2" }),
1074
+ isEdit ? "Salva Modifiche" : "Crea Piano"
1075
+ ] })
1076
+ ] })
1077
+ ] })
1078
+ ] });
1079
+ }
1080
+
1081
+ // src/pages/SubscriptionPlansPage.tsx
1082
+ import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1083
+ function SubscriptionPlansPage({ wrapper: Wrapper, header }) {
1084
+ const [showForm, setShowForm] = useState6(false);
1085
+ const [showDelete, setShowDelete] = useState6(false);
1086
+ const [selectedPlan, setSelectedPlan] = useState6(null);
1087
+ const { data, isLoading, refetch } = useSubscriptionPlans(true);
1088
+ const createMutation = useCreateSubscriptionPlan();
1089
+ const updateMutation = useUpdateSubscriptionPlan();
1090
+ const deleteMutation = useDeleteSubscriptionPlan();
1091
+ const plans = data?.items || [];
1092
+ const handleSubmit = (formData) => {
1093
+ if (selectedPlan) {
1094
+ updateMutation.mutate({ id: selectedPlan.id, data: formData }, {
1095
+ onSuccess: () => {
1096
+ toast3.success("Piano aggiornato con successo");
1097
+ setShowForm(false);
1098
+ setSelectedPlan(null);
1099
+ },
1100
+ onError: (err) => toast3.error(`Errore: ${err.message}`)
1101
+ });
1102
+ } else {
1103
+ createMutation.mutate(formData, {
1104
+ onSuccess: () => {
1105
+ toast3.success("Piano creato con successo");
1106
+ setShowForm(false);
1107
+ },
1108
+ onError: (err) => toast3.error(`Errore: ${err.message}`)
1109
+ });
1110
+ }
1111
+ };
1112
+ const handleDelete = () => {
1113
+ if (selectedPlan) {
1114
+ deleteMutation.mutate(selectedPlan.id, {
1115
+ onSuccess: () => {
1116
+ toast3.success("Piano eliminato con successo");
1117
+ setShowDelete(false);
1118
+ setSelectedPlan(null);
1119
+ },
1120
+ onError: (err) => toast3.error(`Errore: ${err.message}`)
1121
+ });
1122
+ }
1123
+ };
1124
+ const content = /* @__PURE__ */ jsxs13(Fragment4, { children: [
1125
+ header,
1126
+ /* @__PURE__ */ jsxs13("div", { className: "space-y-4", children: [
1127
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between", children: [
1128
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
1129
+ /* @__PURE__ */ jsx16(LayoutGrid, { className: "w-4 h-4" }),
1130
+ /* @__PURE__ */ jsxs13("span", { children: [
1131
+ plans.length,
1132
+ " piani configurati"
1133
+ ] })
1134
+ ] }),
1135
+ /* @__PURE__ */ jsxs13("div", { className: "flex gap-2", children: [
1136
+ /* @__PURE__ */ jsx16("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: "Aggiorna" }),
1137
+ /* @__PURE__ */ jsxs13("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: () => {
1138
+ setSelectedPlan(null);
1139
+ setShowForm(true);
1140
+ }, children: [
1141
+ /* @__PURE__ */ jsx16(Plus3, { className: "w-4 h-4 mr-2" }),
1142
+ "Nuovo Piano"
1143
+ ] })
1144
+ ] })
1145
+ ] }),
1146
+ /* @__PURE__ */ jsx16("div", { className: "rounded-lg border bg-card text-card-foreground shadow-sm", children: /* @__PURE__ */ jsx16("div", { className: "p-0", children: /* @__PURE__ */ jsx16(
1147
+ SubscriptionPlansTable,
1148
+ {
1149
+ plans,
1150
+ isLoading,
1151
+ onEdit: (p) => {
1152
+ setSelectedPlan(p);
1153
+ setShowForm(true);
1154
+ },
1155
+ onDelete: (p) => {
1156
+ setSelectedPlan(p);
1157
+ setShowDelete(true);
1158
+ }
1159
+ }
1160
+ ) }) })
1161
+ ] }),
1162
+ /* @__PURE__ */ jsx16(
1163
+ SubscriptionPlanFormDialog,
1164
+ {
1165
+ isOpen: showForm,
1166
+ onOpenChange: setShowForm,
1167
+ plan: selectedPlan,
1168
+ onSubmit: handleSubmit,
1169
+ isPending: createMutation.isPending || updateMutation.isPending
1170
+ }
1171
+ ),
1172
+ /* @__PURE__ */ jsx16(
1173
+ DeleteConfirmDialog,
1174
+ {
1175
+ isOpen: showDelete,
1176
+ onOpenChange: setShowDelete,
1177
+ title: "Eliminare Piano?",
1178
+ description: /* @__PURE__ */ jsxs13(Fragment4, { children: [
1179
+ "Stai per eliminare il piano ",
1180
+ /* @__PURE__ */ jsx16("strong", { children: selectedPlan?.name }),
1181
+ " (",
1182
+ /* @__PURE__ */ jsx16("code", { children: selectedPlan?.slug }),
1183
+ "). Questa azione non pu\xF2 essere annullata."
1184
+ ] }),
1185
+ onConfirm: handleDelete,
1186
+ isDeleting: deleteMutation.isPending,
1187
+ confirmLabel: "Elimina Piano"
1188
+ }
1189
+ )
1190
+ ] });
1191
+ return Wrapper ? /* @__PURE__ */ jsx16(Wrapper, { children: content }) : content;
1192
+ }
1193
+
1194
+ // src/pages/BillingPlansPage.tsx
1195
+ import { toast as toast4 } from "sonner";
1196
+ import { CreditCard, RefreshCw as RefreshCw2, CloudUpload } from "lucide-react";
1197
+ import {
1198
+ useBillingPlans,
1199
+ useSyncBillingPlanToStripe,
1200
+ formatPrice as formatPrice2
1201
+ } from "@growflowstudio/growflowbooking-admin-core";
1202
+ import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1203
+ function BillingPlansPage({ wrapper: Wrapper, header }) {
1204
+ const { data, isLoading, refetch } = useBillingPlans();
1205
+ const syncMutation = useSyncBillingPlanToStripe();
1206
+ const plans = data?.items || [];
1207
+ const content = /* @__PURE__ */ jsxs14(Fragment5, { children: [
1208
+ header,
1209
+ /* @__PURE__ */ jsxs14("div", { className: "space-y-4", children: [
1210
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-between", children: [
1211
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
1212
+ /* @__PURE__ */ jsx17(CreditCard, { className: "w-4 h-4" }),
1213
+ /* @__PURE__ */ jsxs14("span", { children: [
1214
+ plans.length,
1215
+ " piani billing (GrowFlow)"
1216
+ ] })
1217
+ ] }),
1218
+ /* @__PURE__ */ jsxs14("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: [
1219
+ /* @__PURE__ */ jsx17(RefreshCw2, { className: "w-4 h-4 mr-2" }),
1220
+ "Aggiorna"
1221
+ ] })
1222
+ ] }),
1223
+ /* @__PURE__ */ jsx17("div", { className: "rounded-lg border bg-card text-card-foreground shadow-sm", children: /* @__PURE__ */ jsx17("div", { className: "p-0", children: /* @__PURE__ */ jsxs14("table", { className: "w-full", children: [
1224
+ /* @__PURE__ */ jsx17("thead", { children: /* @__PURE__ */ jsxs14("tr", { className: "border-b", children: [
1225
+ /* @__PURE__ */ jsx17("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Nome" }),
1226
+ /* @__PURE__ */ jsx17("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Slug" }),
1227
+ /* @__PURE__ */ jsx17("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Mensile" }),
1228
+ /* @__PURE__ */ jsx17("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Annuale" }),
1229
+ /* @__PURE__ */ jsx17("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Stato" }),
1230
+ /* @__PURE__ */ jsx17("th", { className: "h-12 px-4 text-right align-middle font-medium text-muted-foreground", children: "Azioni" })
1231
+ ] }) }),
1232
+ /* @__PURE__ */ jsx17("tbody", { children: isLoading ? /* @__PURE__ */ jsx17(SkeletonRows, { rows: 5, columns: 6 }) : plans.length === 0 ? /* @__PURE__ */ jsx17("tr", { children: /* @__PURE__ */ jsx17("td", { colSpan: 6, className: "h-24 text-center text-muted-foreground", children: "Nessun piano billing configurato." }) }) : plans.map((plan) => /* @__PURE__ */ jsxs14("tr", { className: "border-b hover:bg-muted/50", children: [
1233
+ /* @__PURE__ */ jsx17("td", { className: "p-4 font-medium", children: plan.name }),
1234
+ /* @__PURE__ */ jsx17("td", { className: "p-4", children: /* @__PURE__ */ jsx17("code", { className: "text-xs bg-muted px-1.5 py-0.5 rounded", children: plan.slug }) }),
1235
+ /* @__PURE__ */ jsx17("td", { className: "p-4 text-sm", children: formatPrice2(plan.priceMonthly, plan.currency) }),
1236
+ /* @__PURE__ */ jsx17("td", { className: "p-4 text-sm", children: formatPrice2(plan.priceYearly, plan.currency) }),
1237
+ /* @__PURE__ */ jsx17("td", { className: "p-4", children: /* @__PURE__ */ jsx17(
1238
+ StatusBadge,
1239
+ {
1240
+ label: plan.isActive ? "Attivo" : "Inattivo",
1241
+ colorClass: plan.isActive ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-400"
1242
+ }
1243
+ ) }),
1244
+ /* @__PURE__ */ jsx17("td", { className: "p-4 text-right", children: /* @__PURE__ */ jsxs14(
1245
+ "button",
1246
+ {
1247
+ className: "inline-flex items-center justify-center rounded-md text-sm h-8 px-2 hover:bg-accent disabled:opacity-50",
1248
+ onClick: () => syncMutation.mutate(plan.id, {
1249
+ onSuccess: () => toast4.success("Piano sincronizzato con Stripe"),
1250
+ onError: (err) => toast4.error(`Errore sync: ${err.message}`)
1251
+ }),
1252
+ disabled: syncMutation.isPending,
1253
+ children: [
1254
+ /* @__PURE__ */ jsx17(CloudUpload, { className: "w-4 h-4 mr-1" }),
1255
+ "Sync"
1256
+ ]
1257
+ }
1258
+ ) })
1259
+ ] }, plan.id)) })
1260
+ ] }) }) })
1261
+ ] })
1262
+ ] });
1263
+ return Wrapper ? /* @__PURE__ */ jsx17(Wrapper, { children: content }) : content;
1264
+ }
1265
+
1266
+ // src/pages/BillingSubscriptionsPage.tsx
1267
+ import { useState as useState7 } from "react";
1268
+ import { toast as toast5 } from "sonner";
1269
+ import { CreditCard as CreditCard2 } from "lucide-react";
1270
+ import {
1271
+ useBillingSubscriptions,
1272
+ useCancelBillingSubscription,
1273
+ formatDate as formatDate4,
1274
+ billingSubscriptionStatusColors as billingSubscriptionStatusColors2,
1275
+ billingSubscriptionStatusLabels
1276
+ } from "@growflowstudio/growflowbooking-admin-core";
1277
+ import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
1278
+ function BillingSubscriptionsPage({ wrapper: Wrapper, header }) {
1279
+ const [search, setSearch] = useState7("");
1280
+ const [statusFilter, setStatusFilter] = useState7("all");
1281
+ const { data, isLoading, refetch } = useBillingSubscriptions({
1282
+ search: search || void 0,
1283
+ status: statusFilter !== "all" ? statusFilter : void 0
1284
+ });
1285
+ const cancelMutation = useCancelBillingSubscription();
1286
+ const subscriptions = data?.items || [];
1287
+ const content = /* @__PURE__ */ jsxs15(Fragment6, { children: [
1288
+ header,
1289
+ /* @__PURE__ */ jsxs15("div", { className: "space-y-4", children: [
1290
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
1291
+ /* @__PURE__ */ jsx18(CreditCard2, { className: "w-4 h-4" }),
1292
+ /* @__PURE__ */ jsxs15("span", { children: [
1293
+ data?.total || 0,
1294
+ " abbonamenti billing"
1295
+ ] })
1296
+ ] }),
1297
+ /* @__PURE__ */ jsx18(
1298
+ FilterBar,
1299
+ {
1300
+ searchPlaceholder: "Cerca abbonamenti...",
1301
+ searchValue: search,
1302
+ onSearchChange: setSearch,
1303
+ statusFilter,
1304
+ onStatusFilterChange: setStatusFilter,
1305
+ statusOptions: [
1306
+ { value: "all", label: "Tutti gli stati" },
1307
+ { value: "active", label: "Attivi" },
1308
+ { value: "trialing", label: "In Prova" },
1309
+ { value: "past_due", label: "Scaduti" },
1310
+ { value: "canceled", label: "Cancellati" }
1311
+ ],
1312
+ onRefresh: () => refetch()
1313
+ }
1314
+ ),
1315
+ /* @__PURE__ */ jsx18("div", { className: "rounded-lg border bg-card text-card-foreground shadow-sm", children: /* @__PURE__ */ jsx18("div", { className: "p-0", children: /* @__PURE__ */ jsxs15("table", { className: "w-full", children: [
1316
+ /* @__PURE__ */ jsx18("thead", { children: /* @__PURE__ */ jsxs15("tr", { className: "border-b", children: [
1317
+ /* @__PURE__ */ jsx18("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Subscriber" }),
1318
+ /* @__PURE__ */ jsx18("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Piano" }),
1319
+ /* @__PURE__ */ jsx18("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Ciclo" }),
1320
+ /* @__PURE__ */ jsx18("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Stato" }),
1321
+ /* @__PURE__ */ jsx18("th", { className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground", children: "Periodo Corrente" }),
1322
+ /* @__PURE__ */ jsx18("th", { className: "h-12 px-4 text-right align-middle font-medium text-muted-foreground", children: "Azioni" })
1323
+ ] }) }),
1324
+ /* @__PURE__ */ jsx18("tbody", { children: isLoading ? /* @__PURE__ */ jsx18(SkeletonRows, { rows: 5, columns: 6 }) : subscriptions.length === 0 ? /* @__PURE__ */ jsx18("tr", { children: /* @__PURE__ */ jsx18("td", { colSpan: 6, className: "h-24 text-center text-muted-foreground", children: "Nessun abbonamento trovato." }) }) : subscriptions.map((sub) => /* @__PURE__ */ jsxs15("tr", { className: "border-b hover:bg-muted/50", children: [
1325
+ /* @__PURE__ */ jsxs15("td", { className: "p-4", children: [
1326
+ /* @__PURE__ */ jsx18("div", { className: "font-medium", children: sub.subscriberName || sub.subscriberEmail }),
1327
+ /* @__PURE__ */ jsx18("div", { className: "text-xs text-muted-foreground", children: sub.subscriberEmail })
1328
+ ] }),
1329
+ /* @__PURE__ */ jsx18("td", { className: "p-4", children: /* @__PURE__ */ jsx18("code", { className: "text-xs bg-muted px-1.5 py-0.5 rounded", children: sub.planSlug }) }),
1330
+ /* @__PURE__ */ jsx18("td", { className: "p-4 text-sm capitalize", children: sub.billingCycle }),
1331
+ /* @__PURE__ */ jsx18("td", { className: "p-4", children: /* @__PURE__ */ jsx18(
1332
+ StatusBadge,
1333
+ {
1334
+ label: billingSubscriptionStatusLabels[sub.status],
1335
+ colorClass: billingSubscriptionStatusColors2[sub.status]
1336
+ }
1337
+ ) }),
1338
+ /* @__PURE__ */ jsx18("td", { className: "p-4 text-sm text-muted-foreground", children: sub.currentPeriodStart && sub.currentPeriodEnd ? `${formatDate4(sub.currentPeriodStart)} - ${formatDate4(sub.currentPeriodEnd)}` : "-" }),
1339
+ /* @__PURE__ */ jsx18("td", { className: "p-4 text-right", children: sub.status === "active" && /* @__PURE__ */ jsx18(
1340
+ "button",
1341
+ {
1342
+ className: "inline-flex items-center justify-center rounded-md text-sm h-8 px-2 hover:bg-accent text-destructive disabled:opacity-50",
1343
+ onClick: () => cancelMutation.mutate({ id: sub.id, atPeriodEnd: true }, {
1344
+ onSuccess: () => toast5.success("Abbonamento cancellato a fine periodo"),
1345
+ onError: (err) => toast5.error(`Errore: ${err.message}`)
1346
+ }),
1347
+ disabled: cancelMutation.isPending,
1348
+ children: "Cancella"
1349
+ }
1350
+ ) })
1351
+ ] }, sub.id)) })
1352
+ ] }) }) })
1353
+ ] })
1354
+ ] });
1355
+ return Wrapper ? /* @__PURE__ */ jsx18(Wrapper, { children: content }) : content;
1356
+ }
1357
+
1358
+ // src/pages/SettingsPage.tsx
1359
+ import { useState as useState8 } from "react";
1360
+ import { toast as toast6 } from "sonner";
1361
+ import { Settings, Loader2 as Loader25, CheckCircle2, XCircle } from "lucide-react";
1362
+ import {
1363
+ useBillingSettings,
1364
+ useUpdateBillingSettings,
1365
+ useTestBillingConnection
1366
+ } from "@growflowstudio/growflowbooking-admin-core";
1367
+ import { Fragment as Fragment7, jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
1368
+ function SettingsPage({ wrapper: Wrapper, header }) {
1369
+ const { data: settings, isLoading } = useBillingSettings();
1370
+ const updateMutation = useUpdateBillingSettings();
1371
+ const testMutation = useTestBillingConnection();
1372
+ const [apiKey, setApiKey] = useState8("");
1373
+ const [webhookSecret, setWebhookSecret] = useState8("");
1374
+ const [mode, setMode] = useState8("test");
1375
+ const [billingUrlTest, setBillingUrlTest] = useState8("");
1376
+ const [billingUrlProduction, setBillingUrlProduction] = useState8("");
1377
+ const handleSave = () => {
1378
+ const data = {};
1379
+ if (apiKey) data.apiKey = apiKey;
1380
+ if (webhookSecret) data.webhookSecret = webhookSecret;
1381
+ if (mode) data.mode = mode;
1382
+ if (billingUrlTest) data.billingUrlTest = billingUrlTest;
1383
+ if (billingUrlProduction) data.billingUrlProduction = billingUrlProduction;
1384
+ updateMutation.mutate(data, {
1385
+ onSuccess: () => {
1386
+ toast6.success("Impostazioni salvate");
1387
+ setApiKey("");
1388
+ setWebhookSecret("");
1389
+ },
1390
+ onError: (err) => toast6.error(`Errore: ${err.message}`)
1391
+ });
1392
+ };
1393
+ const handleTest = () => {
1394
+ testMutation.mutate(void 0, {
1395
+ onSuccess: (result) => {
1396
+ if (result.success) {
1397
+ toast6.success(`Connessione OK \u2014 ${result.plansCount} piani trovati`);
1398
+ } else {
1399
+ toast6.error(`Connessione fallita: ${result.message}`);
1400
+ }
1401
+ },
1402
+ onError: (err) => toast6.error(`Errore: ${err.message}`)
1403
+ });
1404
+ };
1405
+ const content = /* @__PURE__ */ jsxs16(Fragment7, { children: [
1406
+ header,
1407
+ /* @__PURE__ */ jsxs16("div", { className: "space-y-6", children: [
1408
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
1409
+ /* @__PURE__ */ jsx19(Settings, { className: "w-4 h-4" }),
1410
+ /* @__PURE__ */ jsx19("span", { children: "Impostazioni GrowFlow Billing" })
1411
+ ] }),
1412
+ isLoading ? /* @__PURE__ */ jsx19("div", { className: "space-y-4", children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx19("div", { className: "h-12 w-full animate-pulse rounded-md bg-muted" }, i)) }) : /* @__PURE__ */ jsxs16("div", { className: "space-y-6", children: [
1413
+ /* @__PURE__ */ jsxs16("div", { className: "rounded-lg border bg-card p-6 space-y-4", children: [
1414
+ /* @__PURE__ */ jsx19("h3", { className: "font-medium", children: "Stato Connessione" }),
1415
+ /* @__PURE__ */ jsxs16("div", { className: "grid grid-cols-2 gap-4 text-sm", children: [
1416
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", children: [
1417
+ settings?.apiKeyConfigured ? /* @__PURE__ */ jsx19(CheckCircle2, { className: "w-4 h-4 text-green-600" }) : /* @__PURE__ */ jsx19(XCircle, { className: "w-4 h-4 text-red-500" }),
1418
+ /* @__PURE__ */ jsxs16("span", { children: [
1419
+ "API Key: ",
1420
+ settings?.apiKeyMasked || "Non configurata"
1421
+ ] })
1422
+ ] }),
1423
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", children: [
1424
+ settings?.webhookSecretConfigured ? /* @__PURE__ */ jsx19(CheckCircle2, { className: "w-4 h-4 text-green-600" }) : /* @__PURE__ */ jsx19(XCircle, { className: "w-4 h-4 text-red-500" }),
1425
+ /* @__PURE__ */ jsxs16("span", { children: [
1426
+ "Webhook: ",
1427
+ settings?.webhookSecretMasked || "Non configurato"
1428
+ ] })
1429
+ ] }),
1430
+ /* @__PURE__ */ jsxs16("div", { children: [
1431
+ /* @__PURE__ */ jsx19("span", { className: "text-muted-foreground", children: "Modalit\xE0:" }),
1432
+ " ",
1433
+ settings?.mode || "-"
1434
+ ] }),
1435
+ /* @__PURE__ */ jsxs16("div", { children: [
1436
+ /* @__PURE__ */ jsx19("span", { className: "text-muted-foreground", children: "Client ID:" }),
1437
+ " ",
1438
+ settings?.clientId || "-"
1439
+ ] })
1440
+ ] }),
1441
+ /* @__PURE__ */ jsxs16(
1442
+ "button",
1443
+ {
1444
+ 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 disabled:opacity-50",
1445
+ onClick: handleTest,
1446
+ disabled: testMutation.isPending,
1447
+ children: [
1448
+ testMutation.isPending && /* @__PURE__ */ jsx19(Loader25, { className: "w-4 h-4 animate-spin mr-2" }),
1449
+ "Testa Connessione"
1450
+ ]
1451
+ }
1452
+ )
1453
+ ] }),
1454
+ /* @__PURE__ */ jsxs16("div", { className: "rounded-lg border bg-card p-6 space-y-4", children: [
1455
+ /* @__PURE__ */ jsx19("h3", { className: "font-medium", children: "Aggiorna Impostazioni" }),
1456
+ /* @__PURE__ */ jsxs16("div", { className: "grid gap-4", children: [
1457
+ /* @__PURE__ */ jsxs16("div", { className: "space-y-2", children: [
1458
+ /* @__PURE__ */ jsx19("label", { className: "text-sm font-medium", children: "API Key" }),
1459
+ /* @__PURE__ */ jsx19("input", { type: "password", 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: apiKey, onChange: (e) => setApiKey(e.target.value), placeholder: "sk_live_..." })
1460
+ ] }),
1461
+ /* @__PURE__ */ jsxs16("div", { className: "space-y-2", children: [
1462
+ /* @__PURE__ */ jsx19("label", { className: "text-sm font-medium", children: "Webhook Secret" }),
1463
+ /* @__PURE__ */ jsx19("input", { type: "password", 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: webhookSecret, onChange: (e) => setWebhookSecret(e.target.value), placeholder: "whsec_..." })
1464
+ ] }),
1465
+ /* @__PURE__ */ jsxs16("div", { className: "grid grid-cols-2 gap-4", children: [
1466
+ /* @__PURE__ */ jsxs16("div", { className: "space-y-2", children: [
1467
+ /* @__PURE__ */ jsx19("label", { className: "text-sm font-medium", children: "URL Test" }),
1468
+ /* @__PURE__ */ jsx19("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: billingUrlTest, onChange: (e) => setBillingUrlTest(e.target.value), placeholder: "http://localhost:8008" })
1469
+ ] }),
1470
+ /* @__PURE__ */ jsxs16("div", { className: "space-y-2", children: [
1471
+ /* @__PURE__ */ jsx19("label", { className: "text-sm font-medium", children: "URL Produzione" }),
1472
+ /* @__PURE__ */ jsx19("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: billingUrlProduction, onChange: (e) => setBillingUrlProduction(e.target.value), placeholder: "https://billing.growflow.studio" })
1473
+ ] })
1474
+ ] }),
1475
+ /* @__PURE__ */ jsxs16("div", { className: "space-y-2", children: [
1476
+ /* @__PURE__ */ jsx19("label", { className: "text-sm font-medium", children: "Modalit\xE0" }),
1477
+ /* @__PURE__ */ jsxs16("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: mode, onChange: (e) => setMode(e.target.value), children: [
1478
+ /* @__PURE__ */ jsx19("option", { value: "test", children: "Test" }),
1479
+ /* @__PURE__ */ jsx19("option", { value: "production", children: "Produzione" })
1480
+ ] })
1481
+ ] })
1482
+ ] }),
1483
+ /* @__PURE__ */ jsxs16(
1484
+ "button",
1485
+ {
1486
+ 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",
1487
+ onClick: handleSave,
1488
+ disabled: updateMutation.isPending,
1489
+ children: [
1490
+ updateMutation.isPending && /* @__PURE__ */ jsx19(Loader25, { className: "w-4 h-4 animate-spin mr-2" }),
1491
+ "Salva Impostazioni"
1492
+ ]
1493
+ }
1494
+ )
1495
+ ] })
1496
+ ] })
1497
+ ] })
1498
+ ] });
1499
+ return Wrapper ? /* @__PURE__ */ jsx19(Wrapper, { children: content }) : content;
1500
+ }
1501
+
1502
+ // src/nav/bookingNavItems.ts
1503
+ var ALL_NAV_ITEMS = [
1504
+ { key: "tenants", label: "Tenant", icon: "Building2", href: "/admin/tenants" },
1505
+ { key: "customers", label: "Clienti", icon: "Users", href: "/admin/customers" },
1506
+ { key: "subscriptionPlans", label: "Piani", icon: "LayoutGrid", href: "/admin/subscription-plans" },
1507
+ { key: "billingPlans", label: "Billing Piani", icon: "CreditCard", href: "/admin/billing-plans" },
1508
+ { key: "billingSubscriptions", label: "Abbonamenti", icon: "Receipt", href: "/admin/billing-subscriptions" },
1509
+ { key: "settings", label: "Impostazioni", icon: "Settings", href: "/admin/settings" }
1510
+ ];
1511
+ function getBookingNavItems(modules, basePath) {
1512
+ const enabled = {
1513
+ tenants: true,
1514
+ customers: true,
1515
+ subscriptionPlans: true,
1516
+ billingPlans: true,
1517
+ billingSubscriptions: true,
1518
+ settings: true,
1519
+ ...modules
1520
+ };
1521
+ return ALL_NAV_ITEMS.filter((item) => enabled[item.key] !== false).map((item) => ({
1522
+ ...item,
1523
+ href: basePath ? `${basePath}${item.href}` : item.href
1524
+ }));
1525
+ }
1526
+ export {
1527
+ BillingPlansPage,
1528
+ BillingSubscriptionsPage,
1529
+ BookingAdminProvider,
1530
+ CustomerFormDialog,
1531
+ CustomersPage,
1532
+ CustomersTable,
1533
+ DeleteConfirmDialog,
1534
+ FilterBar,
1535
+ Pagination,
1536
+ SettingsPage,
1537
+ SkeletonRows,
1538
+ StatusBadge,
1539
+ SubscriptionPlanFormDialog,
1540
+ SubscriptionPlansPage,
1541
+ SubscriptionPlansTable,
1542
+ TenantDetailDialog,
1543
+ TenantFormDialog,
1544
+ TenantsPage,
1545
+ TenantsTable,
1546
+ getBookingNavItems,
1547
+ useBookingAdminConfig,
1548
+ useEnabledModules
1549
+ };
1550
+ //# sourceMappingURL=index.mjs.map