@omnibase/shadcn 0.1.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/README.md +73 -0
- package/dist/index.cjs +787 -0
- package/dist/index.css +119 -0
- package/dist/index.d.cts +52 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +748 -0
- package/package.json +75 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
// src/components/ui/card.tsx
|
|
2
|
+
import "react";
|
|
3
|
+
|
|
4
|
+
// src/lib/utils.ts
|
|
5
|
+
import { clsx } from "clsx";
|
|
6
|
+
import { twMerge } from "tailwind-merge";
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/components/ui/card.tsx
|
|
12
|
+
import { jsx } from "react/jsx-runtime";
|
|
13
|
+
function Card({ className, ...props }) {
|
|
14
|
+
return /* @__PURE__ */ jsx(
|
|
15
|
+
"div",
|
|
16
|
+
{
|
|
17
|
+
"data-slot": "card",
|
|
18
|
+
className: cn(
|
|
19
|
+
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
|
20
|
+
className
|
|
21
|
+
),
|
|
22
|
+
...props
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
function CardHeader({ className, ...props }) {
|
|
27
|
+
return /* @__PURE__ */ jsx(
|
|
28
|
+
"div",
|
|
29
|
+
{
|
|
30
|
+
"data-slot": "card-header",
|
|
31
|
+
className: cn(
|
|
32
|
+
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
|
33
|
+
className
|
|
34
|
+
),
|
|
35
|
+
...props
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
function CardTitle({ className, ...props }) {
|
|
40
|
+
return /* @__PURE__ */ jsx(
|
|
41
|
+
"div",
|
|
42
|
+
{
|
|
43
|
+
"data-slot": "card-title",
|
|
44
|
+
className: cn("leading-none font-semibold", className),
|
|
45
|
+
...props
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
function CardDescription({ className, ...props }) {
|
|
50
|
+
return /* @__PURE__ */ jsx(
|
|
51
|
+
"div",
|
|
52
|
+
{
|
|
53
|
+
"data-slot": "card-description",
|
|
54
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
55
|
+
...props
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
function CardContent({ className, ...props }) {
|
|
60
|
+
return /* @__PURE__ */ jsx(
|
|
61
|
+
"div",
|
|
62
|
+
{
|
|
63
|
+
"data-slot": "card-content",
|
|
64
|
+
className: cn("px-6", className),
|
|
65
|
+
...props
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function CardFooter({ className, ...props }) {
|
|
70
|
+
return /* @__PURE__ */ jsx(
|
|
71
|
+
"div",
|
|
72
|
+
{
|
|
73
|
+
"data-slot": "card-footer",
|
|
74
|
+
className: cn("flex items-center px-6 [.border-t]:pt-6", className),
|
|
75
|
+
...props
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/components/ui/button.tsx
|
|
81
|
+
import "react";
|
|
82
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
83
|
+
import { cva } from "class-variance-authority";
|
|
84
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
85
|
+
var buttonVariants = cva(
|
|
86
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
87
|
+
{
|
|
88
|
+
variants: {
|
|
89
|
+
variant: {
|
|
90
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
91
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
92
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
93
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
94
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
95
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
96
|
+
},
|
|
97
|
+
size: {
|
|
98
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
99
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
100
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
101
|
+
icon: "size-9"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
defaultVariants: {
|
|
105
|
+
variant: "default",
|
|
106
|
+
size: "default"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
function Button({
|
|
111
|
+
className,
|
|
112
|
+
variant,
|
|
113
|
+
size,
|
|
114
|
+
asChild = false,
|
|
115
|
+
...props
|
|
116
|
+
}) {
|
|
117
|
+
const Comp = asChild ? Slot : "button";
|
|
118
|
+
return /* @__PURE__ */ jsx2(
|
|
119
|
+
Comp,
|
|
120
|
+
{
|
|
121
|
+
"data-slot": "button",
|
|
122
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
123
|
+
...props
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/components/ui/input.tsx
|
|
129
|
+
import "react";
|
|
130
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
131
|
+
function Input({ className, type, ...props }) {
|
|
132
|
+
return /* @__PURE__ */ jsx3(
|
|
133
|
+
"input",
|
|
134
|
+
{
|
|
135
|
+
type,
|
|
136
|
+
"data-slot": "input",
|
|
137
|
+
className: cn(
|
|
138
|
+
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
139
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
140
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
141
|
+
className
|
|
142
|
+
),
|
|
143
|
+
...props
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/components/ui/label.tsx
|
|
149
|
+
import "react";
|
|
150
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
151
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
152
|
+
function Label({
|
|
153
|
+
className,
|
|
154
|
+
...props
|
|
155
|
+
}) {
|
|
156
|
+
return /* @__PURE__ */ jsx4(
|
|
157
|
+
LabelPrimitive.Root,
|
|
158
|
+
{
|
|
159
|
+
"data-slot": "label",
|
|
160
|
+
className: cn(
|
|
161
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
162
|
+
className
|
|
163
|
+
),
|
|
164
|
+
...props
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/form/index.tsx
|
|
170
|
+
import { jsx as jsx5, jsxs } from "react/jsx-runtime";
|
|
171
|
+
function isUiNodeInputAttributes(attributes) {
|
|
172
|
+
return attributes && typeof attributes === "object" && "name" in attributes && "type" in attributes;
|
|
173
|
+
}
|
|
174
|
+
function CustomFlowForm({ flow, Header }) {
|
|
175
|
+
const hasSubmitButton = flow.ui.nodes.some(
|
|
176
|
+
(node) => isUiNodeInputAttributes(node.attributes) && node.attributes.type === "submit"
|
|
177
|
+
);
|
|
178
|
+
return /* @__PURE__ */ jsx5("div", { children: /* @__PURE__ */ jsx5(Card, { className: "w-full max-w-md mx-auto", children: /* @__PURE__ */ jsxs("form", { action: flow.ui.action, method: flow.ui.method, children: [
|
|
179
|
+
Header && /* @__PURE__ */ jsx5(CardHeader, { children: /* @__PURE__ */ jsx5(CardTitle, { className: "text-center pb-4", children: Header }) }),
|
|
180
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
|
|
181
|
+
flow.ui.nodes.map((node) => {
|
|
182
|
+
if (isUiNodeInputAttributes(node.attributes)) {
|
|
183
|
+
const isSubmitButton = node.attributes.type === "submit";
|
|
184
|
+
const isHiddenField = node.attributes.type === "hidden";
|
|
185
|
+
const isVisibleField = !isHiddenField && !isSubmitButton;
|
|
186
|
+
if (isHiddenField) {
|
|
187
|
+
return /* @__PURE__ */ jsx5(
|
|
188
|
+
"input",
|
|
189
|
+
{
|
|
190
|
+
name: node.attributes.name,
|
|
191
|
+
type: "hidden",
|
|
192
|
+
value: node.attributes.value || "",
|
|
193
|
+
readOnly: true
|
|
194
|
+
},
|
|
195
|
+
node.attributes.name
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
if (isSubmitButton) {
|
|
199
|
+
return /* @__PURE__ */ jsx5(
|
|
200
|
+
Button,
|
|
201
|
+
{
|
|
202
|
+
type: "submit",
|
|
203
|
+
name: node.attributes.name,
|
|
204
|
+
value: node.attributes.value || "",
|
|
205
|
+
className: "w-full mt-2",
|
|
206
|
+
children: node.meta.label?.text || node.attributes.value || "Submit"
|
|
207
|
+
},
|
|
208
|
+
node.attributes.name
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
if (isVisibleField && [
|
|
212
|
+
"default",
|
|
213
|
+
"password",
|
|
214
|
+
"code",
|
|
215
|
+
"webauthn",
|
|
216
|
+
"passkey",
|
|
217
|
+
"totp",
|
|
218
|
+
"lookup_secret"
|
|
219
|
+
].includes(node.group)) {
|
|
220
|
+
return /* @__PURE__ */ jsxs(
|
|
221
|
+
"div",
|
|
222
|
+
{
|
|
223
|
+
className: "space-y-2",
|
|
224
|
+
children: [
|
|
225
|
+
/* @__PURE__ */ jsxs(Label, { htmlFor: node.attributes.name, children: [
|
|
226
|
+
node.meta.label?.text,
|
|
227
|
+
node.attributes.required && /* @__PURE__ */ jsx5("span", { className: "text-destructive ml-1", children: "*" })
|
|
228
|
+
] }),
|
|
229
|
+
/* @__PURE__ */ jsx5(
|
|
230
|
+
Input,
|
|
231
|
+
{
|
|
232
|
+
id: node.attributes.name,
|
|
233
|
+
name: node.attributes.name,
|
|
234
|
+
type: node.attributes.type,
|
|
235
|
+
defaultValue: node.attributes.value || "",
|
|
236
|
+
required: node.attributes.required,
|
|
237
|
+
placeholder: `Enter your ${node.meta.label?.text?.toLowerCase() || node.attributes.name}`
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
node.meta.label?.id || node.attributes.name
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return null;
|
|
247
|
+
}),
|
|
248
|
+
!hasSubmitButton && /* @__PURE__ */ jsx5(Button, { type: "submit", className: "w-full", children: "Submit" })
|
|
249
|
+
] })
|
|
250
|
+
] }) }) });
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/tenant-switcher/index.tsx
|
|
254
|
+
import * as React6 from "react";
|
|
255
|
+
|
|
256
|
+
// src/components/ui/select.tsx
|
|
257
|
+
import * as React5 from "react";
|
|
258
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
259
|
+
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
|
260
|
+
import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
261
|
+
var Select = SelectPrimitive.Root;
|
|
262
|
+
var SelectValue = SelectPrimitive.Value;
|
|
263
|
+
var SelectTrigger = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs2(
|
|
264
|
+
SelectPrimitive.Trigger,
|
|
265
|
+
{
|
|
266
|
+
ref,
|
|
267
|
+
className: cn(
|
|
268
|
+
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
269
|
+
className
|
|
270
|
+
),
|
|
271
|
+
...props,
|
|
272
|
+
children: [
|
|
273
|
+
children,
|
|
274
|
+
/* @__PURE__ */ jsx6(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx6(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
|
|
275
|
+
]
|
|
276
|
+
}
|
|
277
|
+
));
|
|
278
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
279
|
+
var SelectScrollUpButton = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
280
|
+
SelectPrimitive.ScrollUpButton,
|
|
281
|
+
{
|
|
282
|
+
ref,
|
|
283
|
+
className: cn(
|
|
284
|
+
"flex cursor-default items-center justify-center py-1",
|
|
285
|
+
className
|
|
286
|
+
),
|
|
287
|
+
...props,
|
|
288
|
+
children: /* @__PURE__ */ jsx6(ChevronUp, { className: "h-4 w-4" })
|
|
289
|
+
}
|
|
290
|
+
));
|
|
291
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
292
|
+
var SelectScrollDownButton = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
293
|
+
SelectPrimitive.ScrollDownButton,
|
|
294
|
+
{
|
|
295
|
+
ref,
|
|
296
|
+
className: cn(
|
|
297
|
+
"flex cursor-default items-center justify-center py-1",
|
|
298
|
+
className
|
|
299
|
+
),
|
|
300
|
+
...props,
|
|
301
|
+
children: /* @__PURE__ */ jsx6(ChevronDown, { className: "h-4 w-4" })
|
|
302
|
+
}
|
|
303
|
+
));
|
|
304
|
+
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
305
|
+
var SelectContent = React5.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx6(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs2(
|
|
306
|
+
SelectPrimitive.Content,
|
|
307
|
+
{
|
|
308
|
+
ref,
|
|
309
|
+
className: cn(
|
|
310
|
+
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
311
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
312
|
+
className
|
|
313
|
+
),
|
|
314
|
+
position,
|
|
315
|
+
...props,
|
|
316
|
+
children: [
|
|
317
|
+
/* @__PURE__ */ jsx6(SelectScrollUpButton, {}),
|
|
318
|
+
/* @__PURE__ */ jsx6(
|
|
319
|
+
SelectPrimitive.Viewport,
|
|
320
|
+
{
|
|
321
|
+
className: cn(
|
|
322
|
+
"p-1",
|
|
323
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
324
|
+
),
|
|
325
|
+
children
|
|
326
|
+
}
|
|
327
|
+
),
|
|
328
|
+
/* @__PURE__ */ jsx6(SelectScrollDownButton, {})
|
|
329
|
+
]
|
|
330
|
+
}
|
|
331
|
+
) }));
|
|
332
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
333
|
+
var SelectLabel = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
334
|
+
SelectPrimitive.Label,
|
|
335
|
+
{
|
|
336
|
+
ref,
|
|
337
|
+
className: cn("px-2 py-1.5 text-sm font-semibold", className),
|
|
338
|
+
...props
|
|
339
|
+
}
|
|
340
|
+
));
|
|
341
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
342
|
+
var SelectItem = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs2(
|
|
343
|
+
SelectPrimitive.Item,
|
|
344
|
+
{
|
|
345
|
+
ref,
|
|
346
|
+
className: cn(
|
|
347
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
348
|
+
className
|
|
349
|
+
),
|
|
350
|
+
...props,
|
|
351
|
+
children: [
|
|
352
|
+
/* @__PURE__ */ jsx6("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx6(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx6(Check, { className: "h-4 w-4" }) }) }),
|
|
353
|
+
/* @__PURE__ */ jsx6(SelectPrimitive.ItemText, { children })
|
|
354
|
+
]
|
|
355
|
+
}
|
|
356
|
+
));
|
|
357
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
358
|
+
var SelectSeparator = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
359
|
+
SelectPrimitive.Separator,
|
|
360
|
+
{
|
|
361
|
+
ref,
|
|
362
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
363
|
+
...props
|
|
364
|
+
}
|
|
365
|
+
));
|
|
366
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
367
|
+
|
|
368
|
+
// src/tenant-switcher/index.tsx
|
|
369
|
+
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
370
|
+
function SwitchActiveTenant({
|
|
371
|
+
tenants,
|
|
372
|
+
currentTenantId,
|
|
373
|
+
formAction,
|
|
374
|
+
placeholder = "Select tenant...",
|
|
375
|
+
className,
|
|
376
|
+
onTenantChange
|
|
377
|
+
}) {
|
|
378
|
+
const [isLoading, setIsLoading] = React6.useState(false);
|
|
379
|
+
const handleTenantChange = async (tenantId) => {
|
|
380
|
+
if (tenantId === currentTenantId) return;
|
|
381
|
+
setIsLoading(true);
|
|
382
|
+
try {
|
|
383
|
+
onTenantChange?.(tenantId);
|
|
384
|
+
if (formAction) {
|
|
385
|
+
const formData = new FormData();
|
|
386
|
+
formData.append("tenant_id", tenantId);
|
|
387
|
+
await formAction(formData);
|
|
388
|
+
}
|
|
389
|
+
} catch (error) {
|
|
390
|
+
console.error("Failed to switch tenant:", error);
|
|
391
|
+
} finally {
|
|
392
|
+
setIsLoading(false);
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
const currentTenant = tenants.find((tenant) => tenant.id === currentTenantId);
|
|
396
|
+
return /* @__PURE__ */ jsxs3(
|
|
397
|
+
Select,
|
|
398
|
+
{
|
|
399
|
+
value: currentTenantId,
|
|
400
|
+
onValueChange: handleTenantChange,
|
|
401
|
+
disabled: isLoading,
|
|
402
|
+
children: [
|
|
403
|
+
/* @__PURE__ */ jsx7(SelectTrigger, { className: cn("max-w-64", className), children: /* @__PURE__ */ jsx7(SelectValue, { placeholder, children: currentTenant ? currentTenant.name : placeholder }) }),
|
|
404
|
+
/* @__PURE__ */ jsx7(SelectContent, { children: tenants.map((tenant) => /* @__PURE__ */ jsx7(SelectItem, { value: tenant.id, children: tenant.name }, tenant.id)) })
|
|
405
|
+
]
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// src/pricing-table/index.tsx
|
|
411
|
+
import * as React7 from "react";
|
|
412
|
+
import { Check as Check2, Star, ChevronLeft, ChevronRight } from "lucide-react";
|
|
413
|
+
import { Fragment, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
414
|
+
var getCurrencySymbol = (currency) => {
|
|
415
|
+
const symbols = {
|
|
416
|
+
USD: "$",
|
|
417
|
+
EUR: "\u20AC",
|
|
418
|
+
GBP: "\xA3",
|
|
419
|
+
JPY: "\xA5",
|
|
420
|
+
CAD: "C$",
|
|
421
|
+
AUD: "A$"
|
|
422
|
+
};
|
|
423
|
+
return symbols[currency] || currency;
|
|
424
|
+
};
|
|
425
|
+
var formatPrice = (price) => {
|
|
426
|
+
const priceUI = price.ui || {};
|
|
427
|
+
if (priceUI.price_display?.custom_text)
|
|
428
|
+
return priceUI.price_display.custom_text;
|
|
429
|
+
if (!price.amount || price.amount === 0) return "Free";
|
|
430
|
+
const amount = price.amount / 100;
|
|
431
|
+
const currency = price.currency.toUpperCase();
|
|
432
|
+
let formattedPrice = priceUI.price_display?.show_currency !== false ? `${getCurrencySymbol(currency)}${amount.toFixed(2)}` : amount.toFixed(2);
|
|
433
|
+
if (priceUI.price_display?.suffix)
|
|
434
|
+
formattedPrice += ` ${priceUI.price_display.suffix}`;
|
|
435
|
+
return formattedPrice;
|
|
436
|
+
};
|
|
437
|
+
var formatBillingPeriod = (price) => {
|
|
438
|
+
const priceUI = price.ui || {};
|
|
439
|
+
if (priceUI.billing_period) return priceUI.billing_period;
|
|
440
|
+
if (price.interval) {
|
|
441
|
+
const count = price.interval_count || 1;
|
|
442
|
+
return `per ${count === 1 ? price.interval : `${count} ${price.interval}s`}`;
|
|
443
|
+
}
|
|
444
|
+
return "one-time";
|
|
445
|
+
};
|
|
446
|
+
function PricingCard({
|
|
447
|
+
product,
|
|
448
|
+
isSelected,
|
|
449
|
+
onPriceSelect,
|
|
450
|
+
displayedPrice
|
|
451
|
+
}) {
|
|
452
|
+
const ui = product.ui || {};
|
|
453
|
+
const isHighlighted = ui.highlighted;
|
|
454
|
+
return /* @__PURE__ */ jsxs4(
|
|
455
|
+
"div",
|
|
456
|
+
{
|
|
457
|
+
className: cn(
|
|
458
|
+
"flex flex-col h-full pb-6",
|
|
459
|
+
isHighlighted ? "relative" : ""
|
|
460
|
+
),
|
|
461
|
+
children: [
|
|
462
|
+
/* @__PURE__ */ jsx8("div", { className: "h-4 flex-shrink-0 relative", children: ui.badge && /* @__PURE__ */ jsx8("div", { className: "absolute top-0 left-1/2 transform -translate-x-1/2 z-10", children: /* @__PURE__ */ jsxs4("div", { className: "bg-primary text-primary-foreground px-3 py-1 rounded-full text-sm font-medium flex items-center gap-1 whitespace-nowrap shadow-md", children: [
|
|
463
|
+
ui.badge === "Most Popular" && /* @__PURE__ */ jsx8(Star, { className: "w-3 h-3" }),
|
|
464
|
+
ui.badge
|
|
465
|
+
] }) }) }),
|
|
466
|
+
/* @__PURE__ */ jsxs4(
|
|
467
|
+
Card,
|
|
468
|
+
{
|
|
469
|
+
className: cn(
|
|
470
|
+
"flex flex-col flex-1 w-full transition-all duration-200 hover:shadow-lg",
|
|
471
|
+
isHighlighted && "border-primary shadow-lg",
|
|
472
|
+
isSelected && "ring-2 ring-primary"
|
|
473
|
+
),
|
|
474
|
+
children: [
|
|
475
|
+
/* @__PURE__ */ jsxs4(CardHeader, { className: "text-center", children: [
|
|
476
|
+
/* @__PURE__ */ jsx8(CardTitle, { className: "text-xl font-bold", children: ui.display_name || product.name }),
|
|
477
|
+
ui.tagline && /* @__PURE__ */ jsx8(CardDescription, { className: "text-base", children: ui.tagline })
|
|
478
|
+
] }),
|
|
479
|
+
/* @__PURE__ */ jsxs4(CardContent, { className: "flex-1 space-y-6", children: [
|
|
480
|
+
/* @__PURE__ */ jsxs4("div", { className: "text-center", children: [
|
|
481
|
+
/* @__PURE__ */ jsx8("div", { className: "text-3xl font-bold", children: formatPrice(displayedPrice) }),
|
|
482
|
+
/* @__PURE__ */ jsx8("div", { className: "text-sm text-muted-foreground", children: formatBillingPeriod(displayedPrice) })
|
|
483
|
+
] }),
|
|
484
|
+
(ui.features && ui.features.length > 0 || displayedPrice.ui?.features?.length > 0 || displayedPrice.ui?.limits?.length > 0) && /* @__PURE__ */ jsxs4("div", { className: "space-y-4", children: [
|
|
485
|
+
ui.features && ui.features.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "space-y-2", children: [
|
|
486
|
+
/* @__PURE__ */ jsx8("h4", { className: "font-medium text-sm text-muted-foreground uppercase tracking-wide", children: "Features" }),
|
|
487
|
+
/* @__PURE__ */ jsx8("ul", { className: "space-y-2", children: ui.features.map((feature, index) => /* @__PURE__ */ jsxs4("li", { className: "flex items-start gap-2", children: [
|
|
488
|
+
/* @__PURE__ */ jsx8(Check2, { className: "w-4 h-4 text-green-500 mt-0.5 flex-shrink-0" }),
|
|
489
|
+
/* @__PURE__ */ jsx8("span", { className: "text-sm", children: feature })
|
|
490
|
+
] }, index)) })
|
|
491
|
+
] }),
|
|
492
|
+
displayedPrice.ui?.features && displayedPrice.ui.features.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "space-y-2", children: [
|
|
493
|
+
/* @__PURE__ */ jsx8("h4", { className: "font-medium text-sm text-muted-foreground uppercase tracking-wide", children: "This Plan" }),
|
|
494
|
+
/* @__PURE__ */ jsx8("ul", { className: "space-y-2", children: displayedPrice.ui.features.map(
|
|
495
|
+
(feature, index) => /* @__PURE__ */ jsxs4("li", { className: "flex items-start gap-2", children: [
|
|
496
|
+
/* @__PURE__ */ jsx8(Check2, { className: "w-4 h-4 text-blue-500 mt-0.5 flex-shrink-0" }),
|
|
497
|
+
/* @__PURE__ */ jsx8("span", { className: "text-sm", children: feature })
|
|
498
|
+
] }, index)
|
|
499
|
+
) })
|
|
500
|
+
] }),
|
|
501
|
+
displayedPrice.ui?.limits && displayedPrice.ui.limits.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "space-y-2", children: [
|
|
502
|
+
/* @__PURE__ */ jsx8("h4", { className: "font-medium text-sm text-muted-foreground uppercase tracking-wide", children: "Usage Limits" }),
|
|
503
|
+
/* @__PURE__ */ jsx8("ul", { className: "space-y-1", children: displayedPrice.ui.limits.map(
|
|
504
|
+
(limit, index) => /* @__PURE__ */ jsx8(
|
|
505
|
+
"li",
|
|
506
|
+
{
|
|
507
|
+
className: "text-sm text-muted-foreground",
|
|
508
|
+
children: limit.text
|
|
509
|
+
},
|
|
510
|
+
index
|
|
511
|
+
)
|
|
512
|
+
) })
|
|
513
|
+
] })
|
|
514
|
+
] })
|
|
515
|
+
] }),
|
|
516
|
+
/* @__PURE__ */ jsx8(CardFooter, { children: /* @__PURE__ */ jsx8(
|
|
517
|
+
Button,
|
|
518
|
+
{
|
|
519
|
+
className: "w-full",
|
|
520
|
+
variant: isHighlighted ? "default" : "outline",
|
|
521
|
+
size: "lg",
|
|
522
|
+
onClick: () => onPriceSelect?.(displayedPrice.id, product.id),
|
|
523
|
+
children: ui.cta_text || "Choose Plan"
|
|
524
|
+
}
|
|
525
|
+
) })
|
|
526
|
+
]
|
|
527
|
+
}
|
|
528
|
+
)
|
|
529
|
+
]
|
|
530
|
+
}
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
var CARD_WIDTH = 320;
|
|
534
|
+
var GAP = 24;
|
|
535
|
+
function PricingTable({
|
|
536
|
+
products,
|
|
537
|
+
selectedPriceId,
|
|
538
|
+
onPriceSelect,
|
|
539
|
+
className,
|
|
540
|
+
showPricingToggle = false,
|
|
541
|
+
defaultInterval = "month"
|
|
542
|
+
}) {
|
|
543
|
+
const [selectedInterval, setSelectedInterval] = React7.useState(defaultInterval);
|
|
544
|
+
const [carouselIndex, setCarouselIndex] = React7.useState(0);
|
|
545
|
+
const sortedProducts = React7.useMemo(
|
|
546
|
+
() => [...products].sort(
|
|
547
|
+
(a, b) => (a.ui?.sort_order ?? 999) - (b.ui?.sort_order ?? 999)
|
|
548
|
+
),
|
|
549
|
+
[products]
|
|
550
|
+
);
|
|
551
|
+
const hasMultipleIntervals = React7.useMemo(
|
|
552
|
+
() => products.some(
|
|
553
|
+
(p) => new Set(p.prices.map((price) => price.interval)).size > 1
|
|
554
|
+
),
|
|
555
|
+
[products]
|
|
556
|
+
);
|
|
557
|
+
const getDisplayedPrice = (product) => product.prices.find((p) => p.interval === selectedInterval) || product.prices[0];
|
|
558
|
+
const renderCard = (product) => {
|
|
559
|
+
const displayedPrice = getDisplayedPrice(product);
|
|
560
|
+
return /* @__PURE__ */ jsx8(
|
|
561
|
+
PricingCard,
|
|
562
|
+
{
|
|
563
|
+
product,
|
|
564
|
+
displayedPrice,
|
|
565
|
+
isSelected: selectedPriceId === displayedPrice.id,
|
|
566
|
+
onPriceSelect
|
|
567
|
+
}
|
|
568
|
+
);
|
|
569
|
+
};
|
|
570
|
+
const desktopCarousel = /* @__PURE__ */ jsxs4("div", { className: "relative max-w-7xl mx-auto", children: [
|
|
571
|
+
/* @__PURE__ */ jsx8(
|
|
572
|
+
Button,
|
|
573
|
+
{
|
|
574
|
+
variant: "ghost",
|
|
575
|
+
size: "icon",
|
|
576
|
+
className: "absolute left-0 top-1/2 transform -translate-y-1/2 -translate-x-4 z-10 bg-white shadow-lg border hover:bg-gray-50 disabled:opacity-50",
|
|
577
|
+
onClick: () => setCarouselIndex(Math.max(0, carouselIndex - 1)),
|
|
578
|
+
disabled: carouselIndex === 0,
|
|
579
|
+
children: /* @__PURE__ */ jsx8(ChevronLeft, { className: "w-4 h-4" })
|
|
580
|
+
}
|
|
581
|
+
),
|
|
582
|
+
/* @__PURE__ */ jsx8(
|
|
583
|
+
"div",
|
|
584
|
+
{
|
|
585
|
+
className: "overflow-hidden mx-auto",
|
|
586
|
+
style: { width: `${3 * CARD_WIDTH + 2 * GAP}px` },
|
|
587
|
+
children: /* @__PURE__ */ jsx8(
|
|
588
|
+
"div",
|
|
589
|
+
{
|
|
590
|
+
className: "flex items-stretch transition-transform duration-300 ease-in-out",
|
|
591
|
+
style: {
|
|
592
|
+
transform: `translateX(-${carouselIndex * (CARD_WIDTH + GAP)}px)`,
|
|
593
|
+
gap: `${GAP}px`
|
|
594
|
+
},
|
|
595
|
+
children: sortedProducts.map((p) => /* @__PURE__ */ jsx8(
|
|
596
|
+
"div",
|
|
597
|
+
{
|
|
598
|
+
style: { width: `${CARD_WIDTH}px` },
|
|
599
|
+
className: "flex-shrink-0",
|
|
600
|
+
children: renderCard(p)
|
|
601
|
+
},
|
|
602
|
+
p.id
|
|
603
|
+
))
|
|
604
|
+
}
|
|
605
|
+
)
|
|
606
|
+
}
|
|
607
|
+
),
|
|
608
|
+
/* @__PURE__ */ jsx8(
|
|
609
|
+
Button,
|
|
610
|
+
{
|
|
611
|
+
variant: "ghost",
|
|
612
|
+
size: "icon",
|
|
613
|
+
className: "absolute right-0 top-1/2 transform -translate-y-1/2 translate-x-4 z-10 bg-white shadow-lg border hover:bg-gray-50 disabled:opacity-50",
|
|
614
|
+
onClick: () => setCarouselIndex(
|
|
615
|
+
Math.min(sortedProducts.length - 3, carouselIndex + 1)
|
|
616
|
+
),
|
|
617
|
+
disabled: carouselIndex >= sortedProducts.length - 3,
|
|
618
|
+
children: /* @__PURE__ */ jsx8(ChevronRight, { className: "w-4 h-4" })
|
|
619
|
+
}
|
|
620
|
+
),
|
|
621
|
+
/* @__PURE__ */ jsx8("div", { className: "flex justify-center mt-6 space-x-2", children: Array.from(
|
|
622
|
+
{ length: Math.max(1, sortedProducts.length - 2) },
|
|
623
|
+
(_, i) => /* @__PURE__ */ jsx8(
|
|
624
|
+
"button",
|
|
625
|
+
{
|
|
626
|
+
onClick: () => setCarouselIndex(i),
|
|
627
|
+
className: cn(
|
|
628
|
+
"w-2 h-2 rounded-full transition-colors",
|
|
629
|
+
i === carouselIndex ? "bg-primary" : "bg-gray-300"
|
|
630
|
+
),
|
|
631
|
+
"aria-label": `Go to slide ${i + 1}`
|
|
632
|
+
},
|
|
633
|
+
i
|
|
634
|
+
)
|
|
635
|
+
) })
|
|
636
|
+
] });
|
|
637
|
+
const staticLayout = /* @__PURE__ */ jsx8(
|
|
638
|
+
"div",
|
|
639
|
+
{
|
|
640
|
+
className: "flex flex-row items-stretch justify-center",
|
|
641
|
+
style: { gap: `${GAP}px` },
|
|
642
|
+
children: sortedProducts.map((p) => /* @__PURE__ */ jsx8(
|
|
643
|
+
"div",
|
|
644
|
+
{
|
|
645
|
+
style: { width: `${CARD_WIDTH}px` },
|
|
646
|
+
className: "flex-shrink-0",
|
|
647
|
+
children: renderCard(p)
|
|
648
|
+
},
|
|
649
|
+
p.id
|
|
650
|
+
))
|
|
651
|
+
}
|
|
652
|
+
);
|
|
653
|
+
return /* @__PURE__ */ jsxs4("div", { className: cn("w-full", className), children: [
|
|
654
|
+
showPricingToggle && hasMultipleIntervals && /* @__PURE__ */ jsx8("div", { className: "flex justify-center mb-4", children: /* @__PURE__ */ jsxs4("div", { className: "flex bg-gray-100 p-1 rounded-lg", children: [
|
|
655
|
+
/* @__PURE__ */ jsx8(
|
|
656
|
+
Button,
|
|
657
|
+
{
|
|
658
|
+
variant: selectedInterval === "month" ? "default" : "ghost",
|
|
659
|
+
size: "sm",
|
|
660
|
+
onClick: () => setSelectedInterval("month"),
|
|
661
|
+
className: "rounded-md",
|
|
662
|
+
children: "Monthly"
|
|
663
|
+
}
|
|
664
|
+
),
|
|
665
|
+
/* @__PURE__ */ jsx8(
|
|
666
|
+
Button,
|
|
667
|
+
{
|
|
668
|
+
variant: selectedInterval === "year" ? "default" : "ghost",
|
|
669
|
+
size: "sm",
|
|
670
|
+
onClick: () => setSelectedInterval("year"),
|
|
671
|
+
className: "rounded-md",
|
|
672
|
+
children: "Yearly"
|
|
673
|
+
}
|
|
674
|
+
)
|
|
675
|
+
] }) }),
|
|
676
|
+
/* @__PURE__ */ jsxs4("div", { className: "lg:hidden relative", children: [
|
|
677
|
+
/* @__PURE__ */ jsx8(
|
|
678
|
+
"div",
|
|
679
|
+
{
|
|
680
|
+
className: "overflow-hidden mx-auto",
|
|
681
|
+
style: { width: `${CARD_WIDTH}px` },
|
|
682
|
+
children: /* @__PURE__ */ jsx8(
|
|
683
|
+
"div",
|
|
684
|
+
{
|
|
685
|
+
className: "flex transition-transform duration-300 ease-in-out items-stretch",
|
|
686
|
+
style: {
|
|
687
|
+
transform: `translateX(-${carouselIndex * CARD_WIDTH}px)`
|
|
688
|
+
},
|
|
689
|
+
children: sortedProducts.map((product) => /* @__PURE__ */ jsx8(
|
|
690
|
+
"div",
|
|
691
|
+
{
|
|
692
|
+
className: "flex-shrink-0",
|
|
693
|
+
style: { width: `${CARD_WIDTH}px` },
|
|
694
|
+
children: renderCard(product)
|
|
695
|
+
},
|
|
696
|
+
product.id
|
|
697
|
+
))
|
|
698
|
+
}
|
|
699
|
+
)
|
|
700
|
+
}
|
|
701
|
+
),
|
|
702
|
+
sortedProducts.length > 1 && /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
703
|
+
/* @__PURE__ */ jsx8(
|
|
704
|
+
Button,
|
|
705
|
+
{
|
|
706
|
+
variant: "ghost",
|
|
707
|
+
size: "icon",
|
|
708
|
+
className: "absolute left-0 top-1/2 -translate-y-1/2 -translate-x-2 z-10 bg-white shadow-lg border hover:bg-gray-50 disabled:opacity-50",
|
|
709
|
+
onClick: () => setCarouselIndex((prev) => Math.max(0, prev - 1)),
|
|
710
|
+
disabled: carouselIndex === 0,
|
|
711
|
+
children: /* @__PURE__ */ jsx8(ChevronLeft, { className: "w-5 h-5" })
|
|
712
|
+
}
|
|
713
|
+
),
|
|
714
|
+
/* @__PURE__ */ jsx8(
|
|
715
|
+
Button,
|
|
716
|
+
{
|
|
717
|
+
variant: "ghost",
|
|
718
|
+
size: "icon",
|
|
719
|
+
className: "absolute right-0 top-1/2 -translate-y-1/2 translate-x-2 z-10 bg-white shadow-lg border hover:bg-gray-50 disabled:opacity-50",
|
|
720
|
+
onClick: () => setCarouselIndex(
|
|
721
|
+
(prev) => Math.min(sortedProducts.length - 1, prev + 1)
|
|
722
|
+
),
|
|
723
|
+
disabled: carouselIndex >= sortedProducts.length - 1,
|
|
724
|
+
children: /* @__PURE__ */ jsx8(ChevronRight, { className: "w-5 h-5" })
|
|
725
|
+
}
|
|
726
|
+
),
|
|
727
|
+
/* @__PURE__ */ jsx8("div", { className: "flex justify-center mt-6 space-x-2", children: Array.from({ length: sortedProducts.length }, (_, i) => /* @__PURE__ */ jsx8(
|
|
728
|
+
"button",
|
|
729
|
+
{
|
|
730
|
+
onClick: () => setCarouselIndex(i),
|
|
731
|
+
className: cn(
|
|
732
|
+
"w-2 h-2 rounded-full transition-colors",
|
|
733
|
+
i === carouselIndex ? "bg-primary" : "bg-gray-300"
|
|
734
|
+
),
|
|
735
|
+
"aria-label": `Go to slide ${i + 1}`
|
|
736
|
+
},
|
|
737
|
+
i
|
|
738
|
+
)) })
|
|
739
|
+
] })
|
|
740
|
+
] }),
|
|
741
|
+
/* @__PURE__ */ jsx8("div", { className: "hidden lg:block", children: sortedProducts.length <= 3 ? staticLayout : desktopCarousel })
|
|
742
|
+
] });
|
|
743
|
+
}
|
|
744
|
+
export {
|
|
745
|
+
CustomFlowForm,
|
|
746
|
+
PricingTable,
|
|
747
|
+
SwitchActiveTenant
|
|
748
|
+
};
|