@omnibase/shadcn 0.2.0 → 0.4.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.cjs CHANGED
@@ -32,7 +32,8 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  CustomFlowForm: () => CustomFlowForm,
34
34
  PricingTable: () => PricingTable,
35
- SwitchActiveTenant: () => SwitchActiveTenant
35
+ SwitchActiveTenant: () => SwitchActiveTenant,
36
+ TenantCreator: () => TenantCreator
36
37
  });
37
38
  module.exports = __toCommonJS(index_exports);
38
39
 
@@ -307,84 +308,144 @@ function isUiNodeInputAttributes(attributes) {
307
308
  return attributes && typeof attributes === "object" && "name" in attributes && "type" in attributes;
308
309
  }
309
310
  function CustomFlowForm({ flow, Header }) {
311
+ const nodesByGroup = flow.ui.nodes.reduce((groups, node) => {
312
+ const group = node.group || "default";
313
+ if (!groups[group]) {
314
+ groups[group] = [];
315
+ }
316
+ groups[group].push(node);
317
+ return groups;
318
+ }, {});
319
+ const oidcNodes = nodesByGroup.oidc || [];
320
+ const regularNodes = Object.entries(nodesByGroup).filter(([group]) => group !== "oidc").flatMap(([, nodes]) => nodes);
321
+ const csrfToken = regularNodes.find(
322
+ (node) => isUiNodeInputAttributes(node.attributes) && node.attributes.name === "csrf_token"
323
+ );
310
324
  const hasSubmitButton = flow.ui.nodes.some(
311
325
  (node) => isUiNodeInputAttributes(node.attributes) && node.attributes.type === "submit"
312
326
  );
313
327
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
314
328
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Messages, { flow }),
315
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Card, { className: "w-full max-w-md mx-auto", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("form", { action: flow.ui.action, method: flow.ui.method, children: [
316
- Header && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardTitle, { className: "text-center pb-4", children: Header }) }),
317
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(CardContent, { className: "space-y-4", children: [
318
- flow.ui.nodes.map((node) => {
319
- if (isUiNodeInputAttributes(node.attributes)) {
320
- const isSubmitButton = node.attributes.type === "submit";
321
- const isHiddenField = node.attributes.type === "hidden";
322
- const isVisibleField = !isHiddenField && !isSubmitButton;
323
- if (isHiddenField) {
324
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
325
- "input",
326
- {
327
- name: node.attributes.name,
328
- type: "hidden",
329
- value: node.attributes.value || "",
330
- readOnly: true
331
- },
332
- node.attributes.name
333
- );
334
- }
335
- if (isSubmitButton) {
336
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
337
- Button,
338
- {
339
- type: "submit",
340
- name: node.attributes.name,
341
- value: node.attributes.value || "",
342
- className: "w-full mt-2",
343
- children: node.meta.label?.text || node.attributes.value || "Submit"
344
- },
345
- node.attributes.name
346
- );
347
- }
348
- if (isVisibleField && [
349
- "default",
350
- "password",
351
- "code",
352
- "webauthn",
353
- "passkey",
354
- "totp",
355
- "lookup_secret"
356
- ].includes(node.group)) {
329
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Card, { className: "w-full max-w-md mx-auto", children: [
330
+ Header && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardTitle, { className: "text-center pb-1", children: Header }) }),
331
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(CardContent, { className: "space-y-6", children: [
332
+ oidcNodes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-3", children: [
333
+ oidcNodes.map((node, index) => {
334
+ if (isUiNodeInputAttributes(node.attributes) && node.attributes.type === "submit") {
357
335
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
358
- "div",
336
+ "form",
359
337
  {
360
- className: "space-y-2",
338
+ action: flow.ui.action,
339
+ method: flow.ui.method,
340
+ className: "w-full",
361
341
  children: [
362
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Label, { htmlFor: node.attributes.name, children: [
363
- node.meta.label?.text,
364
- node.attributes.required && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-destructive ml-1", children: "*" })
365
- ] }),
342
+ csrfToken && isUiNodeInputAttributes(csrfToken.attributes) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
343
+ "input",
344
+ {
345
+ name: csrfToken.attributes.name,
346
+ type: "hidden",
347
+ value: csrfToken.attributes.value || "",
348
+ readOnly: true
349
+ }
350
+ ),
366
351
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
367
- Input,
352
+ Button,
368
353
  {
369
- id: node.attributes.name,
354
+ type: "submit",
370
355
  name: node.attributes.name,
371
- type: node.attributes.type,
372
- defaultValue: node.attributes.value || "",
373
- required: node.attributes.required,
374
- placeholder: `Enter your ${node.meta.label?.text?.toLowerCase() || node.attributes.name}`
356
+ value: node.attributes.value || "",
357
+ variant: "outline",
358
+ className: "w-full",
359
+ children: node.meta.label?.text || node.attributes.value || "Sign in"
375
360
  }
376
361
  )
377
362
  ]
378
363
  },
379
- node.meta.label?.id || node.attributes.name
364
+ `oidc-${index}`
380
365
  );
381
366
  }
382
- }
383
- return null;
384
- }),
385
- !hasSubmitButton && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { type: "submit", className: "w-full", children: "Submit" })
367
+ return null;
368
+ }),
369
+ regularNodes.some(
370
+ (node) => isUiNodeInputAttributes(node.attributes) && !["hidden", "submit"].includes(node.attributes.type)
371
+ ) && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "relative my-6", children: [
372
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "w-full border-t border-border" }) }),
373
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "relative flex justify-center text-xs uppercase", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "bg-background px-3 text-muted-foreground font-medium", children: "Or continue with email" }) })
374
+ ] })
375
+ ] }),
376
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("form", { action: flow.ui.action, method: flow.ui.method, children: [
377
+ regularNodes.map((node) => {
378
+ if (isUiNodeInputAttributes(node.attributes)) {
379
+ const isSubmitButton = node.attributes.type === "submit";
380
+ const isHiddenField = node.attributes.type === "hidden";
381
+ const isVisibleField = !isHiddenField && !isSubmitButton;
382
+ if (isHiddenField) {
383
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
384
+ "input",
385
+ {
386
+ name: node.attributes.name,
387
+ type: "hidden",
388
+ value: node.attributes.value || "",
389
+ readOnly: true
390
+ },
391
+ node.attributes.name
392
+ );
393
+ }
394
+ if (isSubmitButton) {
395
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
396
+ Button,
397
+ {
398
+ type: "submit",
399
+ name: node.attributes.name,
400
+ value: node.attributes.value || "",
401
+ className: "w-full mt-2",
402
+ children: node.meta.label?.text || node.attributes.value || "Submit"
403
+ },
404
+ node.attributes.name
405
+ );
406
+ }
407
+ if (isVisibleField && [
408
+ "default",
409
+ "password",
410
+ "code",
411
+ "webauthn",
412
+ "passkey",
413
+ "totp",
414
+ "lookup_secret",
415
+ "profile"
416
+ ].includes(node.group)) {
417
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
418
+ "div",
419
+ {
420
+ className: "space-y-2 mb-4",
421
+ children: [
422
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Label, { htmlFor: node.attributes.name, children: [
423
+ node.meta.label?.text,
424
+ node.attributes.required && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-destructive ml-1", children: "*" })
425
+ ] }),
426
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
427
+ Input,
428
+ {
429
+ id: node.attributes.name,
430
+ name: node.attributes.name,
431
+ type: node.attributes.type,
432
+ defaultValue: node.attributes.value || "",
433
+ required: node.attributes.required,
434
+ placeholder: `Enter your ${node.meta.label?.text?.toLowerCase() || node.attributes.name}`
435
+ }
436
+ )
437
+ ]
438
+ },
439
+ node.meta.label?.id || node.attributes.name
440
+ );
441
+ }
442
+ }
443
+ return null;
444
+ }),
445
+ !hasSubmitButton && !oidcNodes.length && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { type: "submit", className: "w-full", children: "Submit" })
446
+ ] })
386
447
  ] })
387
- ] }) })
448
+ ] })
388
449
  ] });
389
450
  }
390
451
 
@@ -879,9 +940,169 @@ function PricingTable({
879
940
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "hidden lg:block", children: sortedProducts.length <= 3 ? staticLayout : desktopCarousel })
880
941
  ] });
881
942
  }
943
+
944
+ // src/tenant-creator/index.tsx
945
+ var import_react = require("react");
946
+ var import_jsx_runtime11 = require("react/jsx-runtime");
947
+ function TenantCreator({
948
+ config = {},
949
+ formActions = {},
950
+ className
951
+ }) {
952
+ const [mode, setMode] = (0, import_react.useState)(
953
+ config.defaultMode || "create"
954
+ );
955
+ const [isLoading, setIsLoading] = (0, import_react.useState)(false);
956
+ const [organizationName, setOrganizationName] = (0, import_react.useState)(
957
+ config.createForm?.organizationName?.defaultValue || ""
958
+ );
959
+ const [billingEmail, setBillingEmail] = (0, import_react.useState)(
960
+ config.createForm?.billingEmail?.defaultValue || ""
961
+ );
962
+ const [token, setToken] = (0, import_react.useState)(
963
+ config.joinForm?.token?.defaultValue || ""
964
+ );
965
+ const handleCreateSubmit = async (e) => {
966
+ e.preventDefault();
967
+ if (!formActions.onCreateOrganization) return;
968
+ setIsLoading(true);
969
+ try {
970
+ await formActions.onCreateOrganization({
971
+ organizationName,
972
+ billingEmail
973
+ });
974
+ } finally {
975
+ setIsLoading(false);
976
+ }
977
+ };
978
+ const handleJoinSubmit = async (e) => {
979
+ e.preventDefault();
980
+ if (!formActions.onJoinOrganization) return;
981
+ setIsLoading(true);
982
+ try {
983
+ await formActions.onJoinOrganization({
984
+ token
985
+ });
986
+ } finally {
987
+ setIsLoading(false);
988
+ }
989
+ };
990
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Card, { className: cn("w-full max-w-md mx-auto", className), children: [
991
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(CardHeader, { children: [
992
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CardTitle, { children: "Organization Setup" }),
993
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CardDescription, { children: "Choose how you want to get started with your organization." })
994
+ ] }),
995
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(CardContent, { className: "space-y-6", children: [
996
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "grid grid-cols-2 rounded-lg bg-muted p-1", children: [
997
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
998
+ "label",
999
+ {
1000
+ className: cn(
1001
+ "flex cursor-pointer items-center justify-center rounded-md px-3 py-2 text-sm font-medium transition-all",
1002
+ mode === "create" ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground",
1003
+ isLoading && "pointer-events-none opacity-50"
1004
+ ),
1005
+ children: [
1006
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1007
+ "input",
1008
+ {
1009
+ type: "radio",
1010
+ name: "mode",
1011
+ value: "create",
1012
+ checked: mode === "create",
1013
+ onChange: () => setMode("create"),
1014
+ className: "sr-only",
1015
+ disabled: isLoading
1016
+ }
1017
+ ),
1018
+ config.createOrganizationText || "Create Organization"
1019
+ ]
1020
+ }
1021
+ ),
1022
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1023
+ "label",
1024
+ {
1025
+ className: cn(
1026
+ "flex cursor-pointer items-center justify-center rounded-md px-3 py-2 text-sm font-medium transition-all",
1027
+ mode === "join" ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground",
1028
+ isLoading && "pointer-events-none opacity-50"
1029
+ ),
1030
+ children: [
1031
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1032
+ "input",
1033
+ {
1034
+ type: "radio",
1035
+ name: "mode",
1036
+ value: "join",
1037
+ checked: mode === "join",
1038
+ onChange: () => setMode("join"),
1039
+ className: "sr-only",
1040
+ disabled: isLoading
1041
+ }
1042
+ ),
1043
+ config.joinOrganizationText || "Join Organization"
1044
+ ]
1045
+ }
1046
+ )
1047
+ ] }),
1048
+ mode === "create" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("form", { onSubmit: handleCreateSubmit, className: "space-y-4", children: [
1049
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "space-y-2", children: [
1050
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Label, { htmlFor: "organizationName", children: config.createForm?.organizationName?.label || "Organization Name" }),
1051
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1052
+ Input,
1053
+ {
1054
+ id: "organizationName",
1055
+ type: "text",
1056
+ placeholder: config.createForm?.organizationName?.placeholder || "Enter organization name",
1057
+ value: organizationName,
1058
+ onChange: (e) => setOrganizationName(e.target.value),
1059
+ required: true,
1060
+ disabled: isLoading
1061
+ }
1062
+ )
1063
+ ] }),
1064
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "space-y-2", children: [
1065
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Label, { htmlFor: "billingEmail", children: config.createForm?.billingEmail?.label || "Billing Email" }),
1066
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1067
+ Input,
1068
+ {
1069
+ id: "billingEmail",
1070
+ type: "email",
1071
+ placeholder: config.createForm?.billingEmail?.placeholder || "Enter billing email",
1072
+ value: billingEmail,
1073
+ onChange: (e) => setBillingEmail(e.target.value),
1074
+ required: true,
1075
+ disabled: isLoading
1076
+ }
1077
+ )
1078
+ ] }),
1079
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Button, { type: "submit", className: "w-full", disabled: isLoading, children: isLoading ? "Creating..." : "Create Organization" })
1080
+ ] }),
1081
+ mode === "join" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("form", { onSubmit: handleJoinSubmit, className: "space-y-4", children: [
1082
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "space-y-2", children: [
1083
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Label, { htmlFor: "token", children: config.joinForm?.token?.label || "Invitation Token" }),
1084
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1085
+ Input,
1086
+ {
1087
+ id: "token",
1088
+ type: "text",
1089
+ placeholder: config.joinForm?.token?.placeholder || "Enter invitation token",
1090
+ value: token,
1091
+ onChange: (e) => setToken(e.target.value),
1092
+ required: true,
1093
+ disabled: isLoading
1094
+ }
1095
+ )
1096
+ ] }),
1097
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Button, { type: "submit", className: "w-full", disabled: isLoading, children: isLoading ? "Joining..." : "Join Organization" })
1098
+ ] })
1099
+ ] })
1100
+ ] });
1101
+ }
882
1102
  // Annotate the CommonJS export names for ESM import in node:
883
1103
  0 && (module.exports = {
884
1104
  CustomFlowForm,
885
1105
  PricingTable,
886
- SwitchActiveTenant
1106
+ SwitchActiveTenant,
1107
+ TenantCreator
887
1108
  });
package/dist/index.d.cts CHANGED
@@ -49,4 +49,44 @@ interface PricingTableProps {
49
49
  }
50
50
  declare function PricingTable({ products, selectedPriceId, onPriceSelect, className, showPricingToggle, defaultInterval, }: PricingTableProps): react_jsx_runtime.JSX.Element;
51
51
 
52
- export { CustomFlowForm, type CustomFormProps, type FlowType, PricingTable, type PricingTableProps, SwitchActiveTenant, type SwitchActiveTenantProps };
52
+ interface TenantCreatorConfig {
53
+ createOrganizationText?: string;
54
+ joinOrganizationText?: string;
55
+ defaultMode?: "create" | "join";
56
+ createForm?: {
57
+ organizationName?: {
58
+ label?: string;
59
+ placeholder?: string;
60
+ defaultValue?: string;
61
+ };
62
+ billingEmail?: {
63
+ label?: string;
64
+ placeholder?: string;
65
+ defaultValue?: string;
66
+ };
67
+ };
68
+ joinForm?: {
69
+ token?: {
70
+ label?: string;
71
+ placeholder?: string;
72
+ defaultValue?: string;
73
+ };
74
+ };
75
+ }
76
+ interface TenantCreatorFormActions {
77
+ onCreateOrganization?: (data: {
78
+ organizationName: string;
79
+ billingEmail: string;
80
+ }) => void | Promise<void>;
81
+ onJoinOrganization?: (data: {
82
+ token: string;
83
+ }) => void | Promise<void>;
84
+ }
85
+ interface TenantCreatorProps {
86
+ config?: TenantCreatorConfig;
87
+ formActions?: TenantCreatorFormActions;
88
+ className?: string;
89
+ }
90
+ declare function TenantCreator({ config, formActions, className, }: TenantCreatorProps): react_jsx_runtime.JSX.Element;
91
+
92
+ export { CustomFlowForm, type CustomFormProps, type FlowType, PricingTable, type PricingTableProps, SwitchActiveTenant, type SwitchActiveTenantProps, TenantCreator, type TenantCreatorConfig, type TenantCreatorFormActions, type TenantCreatorProps };
package/dist/index.d.ts CHANGED
@@ -49,4 +49,44 @@ interface PricingTableProps {
49
49
  }
50
50
  declare function PricingTable({ products, selectedPriceId, onPriceSelect, className, showPricingToggle, defaultInterval, }: PricingTableProps): react_jsx_runtime.JSX.Element;
51
51
 
52
- export { CustomFlowForm, type CustomFormProps, type FlowType, PricingTable, type PricingTableProps, SwitchActiveTenant, type SwitchActiveTenantProps };
52
+ interface TenantCreatorConfig {
53
+ createOrganizationText?: string;
54
+ joinOrganizationText?: string;
55
+ defaultMode?: "create" | "join";
56
+ createForm?: {
57
+ organizationName?: {
58
+ label?: string;
59
+ placeholder?: string;
60
+ defaultValue?: string;
61
+ };
62
+ billingEmail?: {
63
+ label?: string;
64
+ placeholder?: string;
65
+ defaultValue?: string;
66
+ };
67
+ };
68
+ joinForm?: {
69
+ token?: {
70
+ label?: string;
71
+ placeholder?: string;
72
+ defaultValue?: string;
73
+ };
74
+ };
75
+ }
76
+ interface TenantCreatorFormActions {
77
+ onCreateOrganization?: (data: {
78
+ organizationName: string;
79
+ billingEmail: string;
80
+ }) => void | Promise<void>;
81
+ onJoinOrganization?: (data: {
82
+ token: string;
83
+ }) => void | Promise<void>;
84
+ }
85
+ interface TenantCreatorProps {
86
+ config?: TenantCreatorConfig;
87
+ formActions?: TenantCreatorFormActions;
88
+ className?: string;
89
+ }
90
+ declare function TenantCreator({ config, formActions, className, }: TenantCreatorProps): react_jsx_runtime.JSX.Element;
91
+
92
+ export { CustomFlowForm, type CustomFormProps, type FlowType, PricingTable, type PricingTableProps, SwitchActiveTenant, type SwitchActiveTenantProps, TenantCreator, type TenantCreatorConfig, type TenantCreatorFormActions, type TenantCreatorProps };
package/dist/index.js CHANGED
@@ -269,84 +269,144 @@ function isUiNodeInputAttributes(attributes) {
269
269
  return attributes && typeof attributes === "object" && "name" in attributes && "type" in attributes;
270
270
  }
271
271
  function CustomFlowForm({ flow, Header }) {
272
+ const nodesByGroup = flow.ui.nodes.reduce((groups, node) => {
273
+ const group = node.group || "default";
274
+ if (!groups[group]) {
275
+ groups[group] = [];
276
+ }
277
+ groups[group].push(node);
278
+ return groups;
279
+ }, {});
280
+ const oidcNodes = nodesByGroup.oidc || [];
281
+ const regularNodes = Object.entries(nodesByGroup).filter(([group]) => group !== "oidc").flatMap(([, nodes]) => nodes);
282
+ const csrfToken = regularNodes.find(
283
+ (node) => isUiNodeInputAttributes(node.attributes) && node.attributes.name === "csrf_token"
284
+ );
272
285
  const hasSubmitButton = flow.ui.nodes.some(
273
286
  (node) => isUiNodeInputAttributes(node.attributes) && node.attributes.type === "submit"
274
287
  );
275
288
  return /* @__PURE__ */ jsxs("div", { children: [
276
289
  /* @__PURE__ */ jsx7(Messages, { flow }),
277
- /* @__PURE__ */ jsx7(Card, { className: "w-full max-w-md mx-auto", children: /* @__PURE__ */ jsxs("form", { action: flow.ui.action, method: flow.ui.method, children: [
278
- Header && /* @__PURE__ */ jsx7(CardHeader, { children: /* @__PURE__ */ jsx7(CardTitle, { className: "text-center pb-4", children: Header }) }),
279
- /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
280
- flow.ui.nodes.map((node) => {
281
- if (isUiNodeInputAttributes(node.attributes)) {
282
- const isSubmitButton = node.attributes.type === "submit";
283
- const isHiddenField = node.attributes.type === "hidden";
284
- const isVisibleField = !isHiddenField && !isSubmitButton;
285
- if (isHiddenField) {
286
- return /* @__PURE__ */ jsx7(
287
- "input",
288
- {
289
- name: node.attributes.name,
290
- type: "hidden",
291
- value: node.attributes.value || "",
292
- readOnly: true
293
- },
294
- node.attributes.name
295
- );
296
- }
297
- if (isSubmitButton) {
298
- return /* @__PURE__ */ jsx7(
299
- Button,
300
- {
301
- type: "submit",
302
- name: node.attributes.name,
303
- value: node.attributes.value || "",
304
- className: "w-full mt-2",
305
- children: node.meta.label?.text || node.attributes.value || "Submit"
306
- },
307
- node.attributes.name
308
- );
309
- }
310
- if (isVisibleField && [
311
- "default",
312
- "password",
313
- "code",
314
- "webauthn",
315
- "passkey",
316
- "totp",
317
- "lookup_secret"
318
- ].includes(node.group)) {
290
+ /* @__PURE__ */ jsxs(Card, { className: "w-full max-w-md mx-auto", children: [
291
+ Header && /* @__PURE__ */ jsx7(CardHeader, { children: /* @__PURE__ */ jsx7(CardTitle, { className: "text-center pb-1", children: Header }) }),
292
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-6", children: [
293
+ oidcNodes.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
294
+ oidcNodes.map((node, index) => {
295
+ if (isUiNodeInputAttributes(node.attributes) && node.attributes.type === "submit") {
319
296
  return /* @__PURE__ */ jsxs(
320
- "div",
297
+ "form",
321
298
  {
322
- className: "space-y-2",
299
+ action: flow.ui.action,
300
+ method: flow.ui.method,
301
+ className: "w-full",
323
302
  children: [
324
- /* @__PURE__ */ jsxs(Label, { htmlFor: node.attributes.name, children: [
325
- node.meta.label?.text,
326
- node.attributes.required && /* @__PURE__ */ jsx7("span", { className: "text-destructive ml-1", children: "*" })
327
- ] }),
303
+ csrfToken && isUiNodeInputAttributes(csrfToken.attributes) && /* @__PURE__ */ jsx7(
304
+ "input",
305
+ {
306
+ name: csrfToken.attributes.name,
307
+ type: "hidden",
308
+ value: csrfToken.attributes.value || "",
309
+ readOnly: true
310
+ }
311
+ ),
328
312
  /* @__PURE__ */ jsx7(
329
- Input,
313
+ Button,
330
314
  {
331
- id: node.attributes.name,
315
+ type: "submit",
332
316
  name: node.attributes.name,
333
- type: node.attributes.type,
334
- defaultValue: node.attributes.value || "",
335
- required: node.attributes.required,
336
- placeholder: `Enter your ${node.meta.label?.text?.toLowerCase() || node.attributes.name}`
317
+ value: node.attributes.value || "",
318
+ variant: "outline",
319
+ className: "w-full",
320
+ children: node.meta.label?.text || node.attributes.value || "Sign in"
337
321
  }
338
322
  )
339
323
  ]
340
324
  },
341
- node.meta.label?.id || node.attributes.name
325
+ `oidc-${index}`
342
326
  );
343
327
  }
344
- }
345
- return null;
346
- }),
347
- !hasSubmitButton && /* @__PURE__ */ jsx7(Button, { type: "submit", className: "w-full", children: "Submit" })
328
+ return null;
329
+ }),
330
+ regularNodes.some(
331
+ (node) => isUiNodeInputAttributes(node.attributes) && !["hidden", "submit"].includes(node.attributes.type)
332
+ ) && /* @__PURE__ */ jsxs("div", { className: "relative my-6", children: [
333
+ /* @__PURE__ */ jsx7("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ jsx7("span", { className: "w-full border-t border-border" }) }),
334
+ /* @__PURE__ */ jsx7("div", { className: "relative flex justify-center text-xs uppercase", children: /* @__PURE__ */ jsx7("span", { className: "bg-background px-3 text-muted-foreground font-medium", children: "Or continue with email" }) })
335
+ ] })
336
+ ] }),
337
+ /* @__PURE__ */ jsxs("form", { action: flow.ui.action, method: flow.ui.method, children: [
338
+ regularNodes.map((node) => {
339
+ if (isUiNodeInputAttributes(node.attributes)) {
340
+ const isSubmitButton = node.attributes.type === "submit";
341
+ const isHiddenField = node.attributes.type === "hidden";
342
+ const isVisibleField = !isHiddenField && !isSubmitButton;
343
+ if (isHiddenField) {
344
+ return /* @__PURE__ */ jsx7(
345
+ "input",
346
+ {
347
+ name: node.attributes.name,
348
+ type: "hidden",
349
+ value: node.attributes.value || "",
350
+ readOnly: true
351
+ },
352
+ node.attributes.name
353
+ );
354
+ }
355
+ if (isSubmitButton) {
356
+ return /* @__PURE__ */ jsx7(
357
+ Button,
358
+ {
359
+ type: "submit",
360
+ name: node.attributes.name,
361
+ value: node.attributes.value || "",
362
+ className: "w-full mt-2",
363
+ children: node.meta.label?.text || node.attributes.value || "Submit"
364
+ },
365
+ node.attributes.name
366
+ );
367
+ }
368
+ if (isVisibleField && [
369
+ "default",
370
+ "password",
371
+ "code",
372
+ "webauthn",
373
+ "passkey",
374
+ "totp",
375
+ "lookup_secret",
376
+ "profile"
377
+ ].includes(node.group)) {
378
+ return /* @__PURE__ */ jsxs(
379
+ "div",
380
+ {
381
+ className: "space-y-2 mb-4",
382
+ children: [
383
+ /* @__PURE__ */ jsxs(Label, { htmlFor: node.attributes.name, children: [
384
+ node.meta.label?.text,
385
+ node.attributes.required && /* @__PURE__ */ jsx7("span", { className: "text-destructive ml-1", children: "*" })
386
+ ] }),
387
+ /* @__PURE__ */ jsx7(
388
+ Input,
389
+ {
390
+ id: node.attributes.name,
391
+ name: node.attributes.name,
392
+ type: node.attributes.type,
393
+ defaultValue: node.attributes.value || "",
394
+ required: node.attributes.required,
395
+ placeholder: `Enter your ${node.meta.label?.text?.toLowerCase() || node.attributes.name}`
396
+ }
397
+ )
398
+ ]
399
+ },
400
+ node.meta.label?.id || node.attributes.name
401
+ );
402
+ }
403
+ }
404
+ return null;
405
+ }),
406
+ !hasSubmitButton && !oidcNodes.length && /* @__PURE__ */ jsx7(Button, { type: "submit", className: "w-full", children: "Submit" })
407
+ ] })
348
408
  ] })
349
- ] }) })
409
+ ] })
350
410
  ] });
351
411
  }
352
412
 
@@ -841,8 +901,168 @@ function PricingTable({
841
901
  /* @__PURE__ */ jsx10("div", { className: "hidden lg:block", children: sortedProducts.length <= 3 ? staticLayout : desktopCarousel })
842
902
  ] });
843
903
  }
904
+
905
+ // src/tenant-creator/index.tsx
906
+ import { useState as useState3 } from "react";
907
+ import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
908
+ function TenantCreator({
909
+ config = {},
910
+ formActions = {},
911
+ className
912
+ }) {
913
+ const [mode, setMode] = useState3(
914
+ config.defaultMode || "create"
915
+ );
916
+ const [isLoading, setIsLoading] = useState3(false);
917
+ const [organizationName, setOrganizationName] = useState3(
918
+ config.createForm?.organizationName?.defaultValue || ""
919
+ );
920
+ const [billingEmail, setBillingEmail] = useState3(
921
+ config.createForm?.billingEmail?.defaultValue || ""
922
+ );
923
+ const [token, setToken] = useState3(
924
+ config.joinForm?.token?.defaultValue || ""
925
+ );
926
+ const handleCreateSubmit = async (e) => {
927
+ e.preventDefault();
928
+ if (!formActions.onCreateOrganization) return;
929
+ setIsLoading(true);
930
+ try {
931
+ await formActions.onCreateOrganization({
932
+ organizationName,
933
+ billingEmail
934
+ });
935
+ } finally {
936
+ setIsLoading(false);
937
+ }
938
+ };
939
+ const handleJoinSubmit = async (e) => {
940
+ e.preventDefault();
941
+ if (!formActions.onJoinOrganization) return;
942
+ setIsLoading(true);
943
+ try {
944
+ await formActions.onJoinOrganization({
945
+ token
946
+ });
947
+ } finally {
948
+ setIsLoading(false);
949
+ }
950
+ };
951
+ return /* @__PURE__ */ jsxs5(Card, { className: cn("w-full max-w-md mx-auto", className), children: [
952
+ /* @__PURE__ */ jsxs5(CardHeader, { children: [
953
+ /* @__PURE__ */ jsx11(CardTitle, { children: "Organization Setup" }),
954
+ /* @__PURE__ */ jsx11(CardDescription, { children: "Choose how you want to get started with your organization." })
955
+ ] }),
956
+ /* @__PURE__ */ jsxs5(CardContent, { className: "space-y-6", children: [
957
+ /* @__PURE__ */ jsxs5("div", { className: "grid grid-cols-2 rounded-lg bg-muted p-1", children: [
958
+ /* @__PURE__ */ jsxs5(
959
+ "label",
960
+ {
961
+ className: cn(
962
+ "flex cursor-pointer items-center justify-center rounded-md px-3 py-2 text-sm font-medium transition-all",
963
+ mode === "create" ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground",
964
+ isLoading && "pointer-events-none opacity-50"
965
+ ),
966
+ children: [
967
+ /* @__PURE__ */ jsx11(
968
+ "input",
969
+ {
970
+ type: "radio",
971
+ name: "mode",
972
+ value: "create",
973
+ checked: mode === "create",
974
+ onChange: () => setMode("create"),
975
+ className: "sr-only",
976
+ disabled: isLoading
977
+ }
978
+ ),
979
+ config.createOrganizationText || "Create Organization"
980
+ ]
981
+ }
982
+ ),
983
+ /* @__PURE__ */ jsxs5(
984
+ "label",
985
+ {
986
+ className: cn(
987
+ "flex cursor-pointer items-center justify-center rounded-md px-3 py-2 text-sm font-medium transition-all",
988
+ mode === "join" ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground",
989
+ isLoading && "pointer-events-none opacity-50"
990
+ ),
991
+ children: [
992
+ /* @__PURE__ */ jsx11(
993
+ "input",
994
+ {
995
+ type: "radio",
996
+ name: "mode",
997
+ value: "join",
998
+ checked: mode === "join",
999
+ onChange: () => setMode("join"),
1000
+ className: "sr-only",
1001
+ disabled: isLoading
1002
+ }
1003
+ ),
1004
+ config.joinOrganizationText || "Join Organization"
1005
+ ]
1006
+ }
1007
+ )
1008
+ ] }),
1009
+ mode === "create" && /* @__PURE__ */ jsxs5("form", { onSubmit: handleCreateSubmit, className: "space-y-4", children: [
1010
+ /* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
1011
+ /* @__PURE__ */ jsx11(Label, { htmlFor: "organizationName", children: config.createForm?.organizationName?.label || "Organization Name" }),
1012
+ /* @__PURE__ */ jsx11(
1013
+ Input,
1014
+ {
1015
+ id: "organizationName",
1016
+ type: "text",
1017
+ placeholder: config.createForm?.organizationName?.placeholder || "Enter organization name",
1018
+ value: organizationName,
1019
+ onChange: (e) => setOrganizationName(e.target.value),
1020
+ required: true,
1021
+ disabled: isLoading
1022
+ }
1023
+ )
1024
+ ] }),
1025
+ /* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
1026
+ /* @__PURE__ */ jsx11(Label, { htmlFor: "billingEmail", children: config.createForm?.billingEmail?.label || "Billing Email" }),
1027
+ /* @__PURE__ */ jsx11(
1028
+ Input,
1029
+ {
1030
+ id: "billingEmail",
1031
+ type: "email",
1032
+ placeholder: config.createForm?.billingEmail?.placeholder || "Enter billing email",
1033
+ value: billingEmail,
1034
+ onChange: (e) => setBillingEmail(e.target.value),
1035
+ required: true,
1036
+ disabled: isLoading
1037
+ }
1038
+ )
1039
+ ] }),
1040
+ /* @__PURE__ */ jsx11(Button, { type: "submit", className: "w-full", disabled: isLoading, children: isLoading ? "Creating..." : "Create Organization" })
1041
+ ] }),
1042
+ mode === "join" && /* @__PURE__ */ jsxs5("form", { onSubmit: handleJoinSubmit, className: "space-y-4", children: [
1043
+ /* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
1044
+ /* @__PURE__ */ jsx11(Label, { htmlFor: "token", children: config.joinForm?.token?.label || "Invitation Token" }),
1045
+ /* @__PURE__ */ jsx11(
1046
+ Input,
1047
+ {
1048
+ id: "token",
1049
+ type: "text",
1050
+ placeholder: config.joinForm?.token?.placeholder || "Enter invitation token",
1051
+ value: token,
1052
+ onChange: (e) => setToken(e.target.value),
1053
+ required: true,
1054
+ disabled: isLoading
1055
+ }
1056
+ )
1057
+ ] }),
1058
+ /* @__PURE__ */ jsx11(Button, { type: "submit", className: "w-full", disabled: isLoading, children: isLoading ? "Joining..." : "Join Organization" })
1059
+ ] })
1060
+ ] })
1061
+ ] });
1062
+ }
844
1063
  export {
845
1064
  CustomFlowForm,
846
1065
  PricingTable,
847
- SwitchActiveTenant
1066
+ SwitchActiveTenant,
1067
+ TenantCreator
848
1068
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnibase/shadcn",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "OmniBase ShadCN UI Package",
5
5
  "type": "module",
6
6
  "exports": {