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