hazo_auth 5.3.1 → 6.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +167 -17
  2. package/SETUP_CHECKLIST.md +99 -7
  3. package/cli-src/cli/generate.ts +10 -1
  4. package/cli-src/cli/validate.ts +4 -0
  5. package/cli-src/lib/auth/auth_types.ts +21 -12
  6. package/cli-src/lib/auth/hazo_get_tenant_auth.server.ts +25 -24
  7. package/cli-src/lib/auth/index.ts +2 -2
  8. package/cli-src/lib/auth/with_auth.server.ts +15 -15
  9. package/cli-src/lib/cookies_config.server.ts +1 -0
  10. package/cli-src/lib/login_config.server.ts +14 -0
  11. package/cli-src/lib/otp_config.server.ts +91 -0
  12. package/cli-src/lib/services/email_service.ts +3 -1
  13. package/cli-src/lib/services/email_template_manifest.ts +17 -0
  14. package/cli-src/lib/services/email_templates/otp_signin_code.html +13 -0
  15. package/cli-src/lib/services/email_templates/otp_signin_code.txt +5 -0
  16. package/cli-src/lib/services/index.ts +8 -2
  17. package/cli-src/lib/services/otp_service.ts +295 -0
  18. package/cli-src/lib/services/session_token_service.ts +4 -1
  19. package/config/hazo_auth_config.example.ini +38 -0
  20. package/dist/cli/generate.d.ts.map +1 -1
  21. package/dist/cli/generate.js +10 -1
  22. package/dist/cli/validate.d.ts.map +1 -1
  23. package/dist/cli/validate.js +4 -0
  24. package/dist/client.d.ts +2 -0
  25. package/dist/client.d.ts.map +1 -1
  26. package/dist/client.js +1 -0
  27. package/dist/components/layouts/login/index.d.ts +7 -1
  28. package/dist/components/layouts/login/index.d.ts.map +1 -1
  29. package/dist/components/layouts/login/index.js +2 -2
  30. package/dist/components/layouts/otp/index.d.ts +10 -0
  31. package/dist/components/layouts/otp/index.d.ts.map +1 -0
  32. package/dist/components/layouts/otp/index.js +14 -0
  33. package/dist/components/layouts/shared/components/sidebar_layout_wrapper.d.ts.map +1 -1
  34. package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +8 -3
  35. package/dist/components/otp/OTPRequestForm.d.ts +11 -0
  36. package/dist/components/otp/OTPRequestForm.d.ts.map +1 -0
  37. package/dist/components/otp/OTPRequestForm.js +42 -0
  38. package/dist/components/otp/OTPVerifyForm.d.ts +16 -0
  39. package/dist/components/otp/OTPVerifyForm.d.ts.map +1 -0
  40. package/dist/components/otp/OTPVerifyForm.js +75 -0
  41. package/dist/components/otp/index.d.ts +5 -0
  42. package/dist/components/otp/index.d.ts.map +1 -0
  43. package/dist/components/otp/index.js +2 -0
  44. package/dist/components/ui/input-otp.d.ts +35 -0
  45. package/dist/components/ui/input-otp.d.ts.map +1 -0
  46. package/dist/components/ui/input-otp.js +44 -0
  47. package/dist/index.d.ts +1 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/lib/auth/auth_types.d.ts +13 -12
  50. package/dist/lib/auth/auth_types.d.ts.map +1 -1
  51. package/dist/lib/auth/auth_types.js +8 -0
  52. package/dist/lib/auth/hazo_get_tenant_auth.server.d.ts +8 -7
  53. package/dist/lib/auth/hazo_get_tenant_auth.server.d.ts.map +1 -1
  54. package/dist/lib/auth/hazo_get_tenant_auth.server.js +23 -22
  55. package/dist/lib/auth/index.d.ts +2 -2
  56. package/dist/lib/auth/index.d.ts.map +1 -1
  57. package/dist/lib/auth/with_auth.server.d.ts +13 -13
  58. package/dist/lib/auth/with_auth.server.d.ts.map +1 -1
  59. package/dist/lib/auth/with_auth.server.js +2 -2
  60. package/dist/lib/cookies_config.server.d.ts +1 -0
  61. package/dist/lib/cookies_config.server.d.ts.map +1 -1
  62. package/dist/lib/cookies_config.server.js +1 -0
  63. package/dist/lib/login_config.server.d.ts +6 -0
  64. package/dist/lib/login_config.server.d.ts.map +1 -1
  65. package/dist/lib/login_config.server.js +7 -0
  66. package/dist/lib/otp_config.server.d.ts +49 -0
  67. package/dist/lib/otp_config.server.d.ts.map +1 -0
  68. package/dist/lib/otp_config.server.js +48 -0
  69. package/dist/lib/services/email_service.d.ts +1 -1
  70. package/dist/lib/services/email_service.d.ts.map +1 -1
  71. package/dist/lib/services/email_service.js +2 -0
  72. package/dist/lib/services/email_template_manifest.d.ts.map +1 -1
  73. package/dist/lib/services/email_template_manifest.js +17 -0
  74. package/dist/lib/services/email_templates/otp_signin_code.html +13 -0
  75. package/dist/lib/services/email_templates/otp_signin_code.txt +5 -0
  76. package/dist/lib/services/index.d.ts +2 -0
  77. package/dist/lib/services/index.d.ts.map +1 -1
  78. package/dist/lib/services/index.js +1 -0
  79. package/dist/lib/services/otp_service.d.ts +46 -0
  80. package/dist/lib/services/otp_service.d.ts.map +1 -0
  81. package/dist/lib/services/otp_service.js +238 -0
  82. package/dist/lib/services/session_token_service.d.ts +3 -1
  83. package/dist/lib/services/session_token_service.d.ts.map +1 -1
  84. package/dist/lib/services/session_token_service.js +4 -2
  85. package/dist/page_components/otp.d.ts +4 -0
  86. package/dist/page_components/otp.d.ts.map +1 -0
  87. package/dist/page_components/otp.js +5 -0
  88. package/dist/server/routes/index.d.ts +2 -0
  89. package/dist/server/routes/index.d.ts.map +1 -1
  90. package/dist/server/routes/index.js +3 -0
  91. package/dist/server/routes/me.d.ts.map +1 -1
  92. package/dist/server/routes/me.js +43 -1
  93. package/dist/server/routes/otp/request.d.ts +3 -0
  94. package/dist/server/routes/otp/request.d.ts.map +1 -0
  95. package/dist/server/routes/otp/request.js +33 -0
  96. package/dist/server/routes/otp/verify.d.ts +3 -0
  97. package/dist/server/routes/otp/verify.d.ts.map +1 -0
  98. package/dist/server/routes/otp/verify.js +58 -0
  99. package/dist/server-lib.d.ts +3 -0
  100. package/dist/server-lib.d.ts.map +1 -1
  101. package/dist/server-lib.js +2 -0
  102. package/dist/server_pages/forgot_password.d.ts +1 -1
  103. package/dist/server_pages/forgot_password.d.ts.map +1 -1
  104. package/dist/server_pages/forgot_password.js +2 -1
  105. package/dist/server_pages/login.d.ts +1 -1
  106. package/dist/server_pages/login.d.ts.map +1 -1
  107. package/dist/server_pages/login.js +3 -2
  108. package/dist/server_pages/login_client_wrapper.d.ts +1 -1
  109. package/dist/server_pages/login_client_wrapper.d.ts.map +1 -1
  110. package/dist/server_pages/login_client_wrapper.js +2 -2
  111. package/dist/server_pages/my_settings.d.ts +1 -1
  112. package/dist/server_pages/my_settings.d.ts.map +1 -1
  113. package/dist/server_pages/my_settings.js +2 -1
  114. package/dist/server_pages/otp.d.ts +42 -0
  115. package/dist/server_pages/otp.d.ts.map +1 -0
  116. package/dist/server_pages/otp.js +38 -0
  117. package/dist/server_pages/register.d.ts +1 -1
  118. package/dist/server_pages/register.d.ts.map +1 -1
  119. package/dist/server_pages/register.js +2 -1
  120. package/dist/server_pages/reset_password.d.ts +1 -1
  121. package/dist/server_pages/reset_password.d.ts.map +1 -1
  122. package/dist/server_pages/reset_password.js +2 -1
  123. package/dist/server_pages/verify_email.d.ts +1 -1
  124. package/dist/server_pages/verify_email.d.ts.map +1 -1
  125. package/dist/server_pages/verify_email.js +2 -1
  126. package/package.json +20 -3
@@ -93,11 +93,20 @@ ${exports}
93
93
  `;
94
94
  }
95
95
  function generate_page_content(page) {
96
+ // Next 16 requires page default exports to satisfy `PageProps` (i.e. accept
97
+ // `{ params?, searchParams? }` and nothing else). Re-exporting the named
98
+ // component directly fails that check because the component carries custom
99
+ // props (image_src, layout, …). The wrapper below is a parameter-less
100
+ // function that returns the component — Next sees a `() => JSX` signature
101
+ // which trivially satisfies PageProps. Direct JSX consumers still use the
102
+ // named export from `hazo_auth/pages` and get the full custom-prop API.
96
103
  return `// Generated by hazo_auth - do not edit manually
97
104
  // Page: /${page.path}
98
105
  import { ${page.component_name} } from "hazo_auth/pages";
99
106
 
100
- export default ${page.component_name};
107
+ export default function Page() {
108
+ return <${page.component_name} />;
109
+ }
101
110
  `;
102
111
  }
103
112
  // section: api_route_generation
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"AAOA,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB,CAAC;AA2rBF,wBAAgB,cAAc,IAAI,iBAAiB,CA2ElD"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"AAOA,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB,CAAC;AA+rBF,wBAAgB,cAAc,IAAI,iBAAiB,CA2ElD"}
@@ -44,6 +44,9 @@ const REQUIRED_API_ROUTES = [
44
44
  { path: "api/hazo_auth/user_management/users/roles", method: "GET" },
45
45
  { path: "api/hazo_auth/user_management/users/roles", method: "POST" },
46
46
  { path: "api/hazo_auth/user_management/users/roles", method: "PUT" },
47
+ // OTP routes
48
+ { path: "api/hazo_auth/otp/request", method: "POST" },
49
+ { path: "api/hazo_auth/otp/verify", method: "POST" },
47
50
  ];
48
51
  // section: helpers
49
52
  function get_project_root() {
@@ -486,6 +489,7 @@ const REQUIRED_TABLES = [
486
489
  "hazo_role_permissions",
487
490
  "hazo_invitations",
488
491
  "hazo_refresh_tokens",
492
+ "hazo_email_otps",
489
493
  ];
490
494
  const TEXT_ID_TABLES = [
491
495
  "hazo_users",
package/dist/client.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from "./components/index.js";
2
+ export { OTPRequestForm, OTPVerifyForm } from "./components/otp.js";
3
+ export type { OTPRequestFormProps, OTPVerifyFormProps } from "./components/otp";
2
4
  export { cn, merge_class_names } from "./lib/utils.js";
3
5
  export * from "./lib/auth/auth_types.js";
4
6
  export { use_auth_status, trigger_auth_status_refresh } from "./components/layouts/shared/hooks/use_auth_status.js";
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAYA,cAAc,oBAAoB,CAAC;AAInC,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIpD,cAAc,uBAAuB,CAAC;AAItC,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,mDAAmD,CAAC;AACjH,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,iDAAiD,CAAC;AAC3G,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,iDAAiD,CAAC;AAC7G,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,qDAAqD,CAAC;AACnH,YAAY,EAAE,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,qDAAqD,CAAC;AAGvI,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAI/E,cAAc,8CAA8C,CAAC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAYA,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjE,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAIhF,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIpD,cAAc,uBAAuB,CAAC;AAItC,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,mDAAmD,CAAC;AACjH,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,iDAAiD,CAAC;AAC3G,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,iDAAiD,CAAC;AAC7G,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,qDAAqD,CAAC;AACnH,YAAY,EAAE,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,qDAAqD,CAAC;AAGvI,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAI/E,cAAc,8CAA8C,CAAC"}
package/dist/client.js CHANGED
@@ -10,6 +10,7 @@
10
10
  // section: component_exports
11
11
  // All UI and layout components are client-safe
12
12
  export * from "./components/index.js";
13
+ export { OTPRequestForm, OTPVerifyForm } from "./components/otp.js";
13
14
  // section: utility_exports
14
15
  // CSS utility functions
15
16
  export { cn, merge_class_names } from "./lib/utils.js";
@@ -43,6 +43,12 @@ export type LoginLayoutProps<TClient = unknown> = {
43
43
  urlOnLogon?: string;
44
44
  /** OAuth configuration */
45
45
  oauth?: OAuthLayoutConfig;
46
+ /** Show the OTP sign-in link below the login form (default: false) */
47
+ otp_signin_enabled?: boolean;
48
+ /** Label for the OTP sign-in link */
49
+ otp_signin_label?: string;
50
+ /** href for the OTP sign-in link */
51
+ otp_signin_href?: string;
46
52
  /**
47
53
  * Layout mode (default: `"two_column"`).
48
54
  * - `"two_column"` — renders the form inside the package's TwoColumnAuthLayout
@@ -55,5 +61,5 @@ export type LoginLayoutProps<TClient = unknown> = {
55
61
  */
56
62
  layout?: "two_column" | "form_only";
57
63
  };
58
- export default function login_layout<TClient>({ image_src, image_alt, image_background_color, field_overrides, labels, button_colors, data_client, logger, redirectRoute, successMessage, alreadyLoggedInMessage, showLogoutButton, showReturnHomeButton, returnHomeButtonLabel, returnHomePath, forgot_password_path, forgot_password_label, create_account_path, create_account_label, show_create_account_link, urlOnLogon, oauth, layout, }: LoginLayoutProps<TClient>): import("react/jsx-runtime").JSX.Element;
64
+ export default function login_layout<TClient>({ image_src, image_alt, image_background_color, field_overrides, labels, button_colors, data_client, logger, redirectRoute, successMessage, alreadyLoggedInMessage, showLogoutButton, showReturnHomeButton, returnHomeButtonLabel, returnHomePath, forgot_password_path, forgot_password_label, create_account_path, create_account_label, show_create_account_link, urlOnLogon, oauth, otp_signin_enabled, otp_signin_label, otp_signin_href, layout, }: LoginLayoutProps<TClient>): import("react/jsx-runtime").JSX.Element;
59
65
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/login/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/login/index.tsx"],"names":[],"mappings":"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"}
@@ -22,7 +22,7 @@ const ORDERED_FIELDS = [
22
22
  LOGIN_FIELD_IDS.PASSWORD,
23
23
  ];
24
24
  // section: component
25
- export default function login_layout({ image_src, image_alt, image_background_color = "#f1f5f9", field_overrides, labels, button_colors, data_client, logger, redirectRoute, successMessage = "Successfully logged in", alreadyLoggedInMessage = "You are already logged in", showLogoutButton = true, showReturnHomeButton = false, returnHomeButtonLabel = "Return home", returnHomePath = "/", forgot_password_path = "/hazo_auth/forgot_password", forgot_password_label = "Forgot password?", create_account_path = "/hazo_auth/register", create_account_label = "Create account", show_create_account_link = true, urlOnLogon, oauth, layout = "two_column", }) {
25
+ export default function login_layout({ image_src, image_alt, image_background_color = "#f1f5f9", field_overrides, labels, button_colors, data_client, logger, redirectRoute, successMessage = "Successfully logged in", alreadyLoggedInMessage = "You are already logged in", showLogoutButton = true, showReturnHomeButton = false, returnHomeButtonLabel = "Return home", returnHomePath = "/", forgot_password_path = "/hazo_auth/forgot_password", forgot_password_label = "Forgot password?", create_account_path = "/hazo_auth/register", create_account_label = "Create account", show_create_account_link = true, urlOnLogon, oauth, otp_signin_enabled = false, otp_signin_label = "Sign in with email code", otp_signin_href = "/hazo_auth/otp", layout = "two_column", }) {
26
26
  var _a;
27
27
  // Default OAuth config: both enabled
28
28
  const oauthConfig = oauth || {
@@ -95,7 +95,7 @@ export default function login_layout({ image_src, image_alt, image_background_co
95
95
  // into TwoColumnAuthLayout's `formContent`; in "form_only" mode it's returned
96
96
  // as the entire output for the consumer to compose into their own chrome.
97
97
  const successContent = (_jsxs(_Fragment, { children: [_jsx(FormHeader, { heading: resolvedLabels.heading, subHeading: resolvedLabels.subHeading }), _jsxs("div", { className: "cls_login_layout_success flex flex-col items-center justify-center gap-4 p-8 text-center", children: [_jsx(CheckCircle, { className: "cls_login_layout_success_icon h-16 w-16 text-green-600", "aria-hidden": "true" }), _jsx("p", { className: "cls_login_layout_success_message text-lg font-medium text-slate-900", children: successMessage })] })] }));
98
- const mainContent = (_jsxs(_Fragment, { children: [_jsx(FormHeader, { heading: resolvedLabels.heading, subHeading: resolvedLabels.subHeading }), oauthError && (_jsxs("div", { className: "cls_login_layout_oauth_error flex items-center gap-2 rounded-md border border-red-200 bg-red-50 p-3 text-sm text-red-700", children: [_jsx(AlertCircle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }), _jsx("span", { children: getOAuthErrorMessage(oauthError) })] })), oauthConfig.enable_google && (_jsx("div", { className: "cls_login_layout_oauth_section", children: _jsx(GoogleSignInButton, { label: oauthConfig.google_button_text, callbackUrl: oauthCallbackUrl }) })), oauthConfig.enable_google && oauthConfig.enable_email_password && (_jsx(OAuthDivider, { text: oauthConfig.oauth_divider_text })), oauthConfig.enable_email_password && (_jsxs("form", { className: "cls_login_layout_form_fields flex flex-col gap-5", onSubmit: form.handleSubmit, "aria-label": "Login form", children: [renderFields(form), _jsx(FormActionButtons, { submitLabel: resolvedLabels.submitButton, cancelLabel: resolvedLabels.cancelButton, buttonPalette: resolvedButtonPalette, isSubmitDisabled: form.isSubmitDisabled, onCancel: form.handleCancel, submitAriaLabel: "Submit login form", cancelAriaLabel: "Cancel login form" }), ((forgot_password_path && forgot_password_label) || (show_create_account_link && create_account_path && create_account_label)) && (_jsxs("div", { className: "cls_login_layout_support_links flex flex-col gap-1 text-sm text-muted-foreground", children: [forgot_password_path && forgot_password_label && (_jsx(Link, { href: forgot_password_path, className: "cls_login_layout_forgot_password_link text-primary underline-offset-4 hover:underline", "aria-label": "Go to forgot password page", children: forgot_password_label })), show_create_account_link && create_account_path && create_account_label && (_jsx(Link, { href: create_account_path, className: "cls_login_layout_create_account_link text-primary underline-offset-4 hover:underline", "aria-label": "Go to create account page", children: create_account_label }))] }))] })), show_create_account_link && create_account_path && create_account_label && !oauthConfig.enable_email_password && oauthConfig.enable_google && (_jsx("div", { className: "cls_login_layout_support_links mt-4 text-center text-sm text-muted-foreground", children: _jsx(Link, { href: create_account_path, className: "cls_login_layout_create_account_link text-primary underline-offset-4 hover:underline", "aria-label": "Go to create account page", children: create_account_label }) }))] }));
98
+ const mainContent = (_jsxs(_Fragment, { children: [_jsx(FormHeader, { heading: resolvedLabels.heading, subHeading: resolvedLabels.subHeading }), oauthError && (_jsxs("div", { className: "cls_login_layout_oauth_error flex items-center gap-2 rounded-md border border-red-200 bg-red-50 p-3 text-sm text-red-700", children: [_jsx(AlertCircle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }), _jsx("span", { children: getOAuthErrorMessage(oauthError) })] })), oauthConfig.enable_google && (_jsx("div", { className: "cls_login_layout_oauth_section", children: _jsx(GoogleSignInButton, { label: oauthConfig.google_button_text, callbackUrl: oauthCallbackUrl }) })), oauthConfig.enable_google && oauthConfig.enable_email_password && (_jsx(OAuthDivider, { text: oauthConfig.oauth_divider_text })), oauthConfig.enable_email_password && (_jsxs("form", { className: "cls_login_layout_form_fields flex flex-col gap-5", onSubmit: form.handleSubmit, "aria-label": "Login form", children: [renderFields(form), _jsx(FormActionButtons, { submitLabel: resolvedLabels.submitButton, cancelLabel: resolvedLabels.cancelButton, buttonPalette: resolvedButtonPalette, isSubmitDisabled: form.isSubmitDisabled, onCancel: form.handleCancel, submitAriaLabel: "Submit login form", cancelAriaLabel: "Cancel login form" }), ((forgot_password_path && forgot_password_label) || (show_create_account_link && create_account_path && create_account_label)) && (_jsxs("div", { className: "cls_login_layout_support_links flex flex-col gap-1 text-sm text-muted-foreground", children: [forgot_password_path && forgot_password_label && (_jsx(Link, { href: forgot_password_path, className: "cls_login_layout_forgot_password_link text-primary underline-offset-4 hover:underline", "aria-label": "Go to forgot password page", children: forgot_password_label })), show_create_account_link && create_account_path && create_account_label && (_jsx(Link, { href: create_account_path, className: "cls_login_layout_create_account_link text-primary underline-offset-4 hover:underline", "aria-label": "Go to create account page", children: create_account_label }))] }))] })), show_create_account_link && create_account_path && create_account_label && !oauthConfig.enable_email_password && oauthConfig.enable_google && (_jsx("div", { className: "cls_login_layout_support_links mt-4 text-center text-sm text-muted-foreground", children: _jsx(Link, { href: create_account_path, className: "cls_login_layout_create_account_link text-primary underline-offset-4 hover:underline", "aria-label": "Go to create account page", children: create_account_label }) })), otp_signin_enabled && otp_signin_href && (_jsx("div", { className: "cls_login_layout_otp_link mt-4 text-center text-sm", children: _jsx("a", { href: otp_signin_href, className: "underline text-muted-foreground hover:text-foreground", "aria-label": "Sign in with email code", children: otp_signin_label !== null && otp_signin_label !== void 0 ? otp_signin_label : "Sign in with email code" }) }))] }));
99
99
  // Form-only mode: return the content directly. Consumer is expected to wrap
100
100
  // it in their own page/brand chrome. We deliberately skip AlreadyLoggedInGuard
101
101
  // here — its 2-col fallback would defeat the purpose of form_only. Consumer
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ export interface OTPLayoutProps {
3
+ /** URL to redirect to after successful sign-in. Defaults to "/" */
4
+ redirect_url?: string;
5
+ /** Page heading. Defaults to "Sign in with email code" */
6
+ title?: string;
7
+ }
8
+ export declare function OTPLayout({ redirect_url, title, }: OTPLayoutProps): React.ReactElement;
9
+ export default OTPLayout;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/otp/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,MAAM,WAAW,cAAc;IAC7B,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAGD,wBAAgB,SAAS,CAAC,EACxB,YAAkB,EAClB,KAAiC,GAClC,EAAE,cAAc,GAAG,KAAK,CAAC,YAAY,CAgBrC;AAED,eAAe,SAAS,CAAC"}
@@ -0,0 +1,14 @@
1
+ // file_description: OTP (email one-time-password) sign-in layout component
2
+ // section: client_directive
3
+ "use client";
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ // section: imports
6
+ import * as React from "react";
7
+ import { OTPRequestForm } from "../../otp/OTPRequestForm.js";
8
+ import { OTPVerifyForm } from "../../otp/OTPVerifyForm.js";
9
+ // section: component
10
+ export function OTPLayout({ redirect_url = "/", title = "Sign in with email code", }) {
11
+ const [sent_to, set_sent_to] = React.useState(null);
12
+ return (_jsxs("div", { className: "mx-auto w-full max-w-sm py-12", children: [_jsx("h1", { className: "text-2xl font-semibold mb-6", children: title }), sent_to ? (_jsx(OTPVerifyForm, { email: sent_to, redirect_url: redirect_url })) : (_jsx(OTPRequestForm, { on_sent: (email) => set_sent_to(email) }))] }));
13
+ }
14
+ export default OTPLayout;
@@ -1 +1 @@
1
- {"version":3,"file":"sidebar_layout_wrapper.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/sidebar_layout_wrapper.tsx"],"names":[],"mappings":"AAwBA,KAAK,yBAAyB,GAAG;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAGF,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,EAAE,yBAAyB,2CA+Q3E"}
1
+ {"version":3,"file":"sidebar_layout_wrapper.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/sidebar_layout_wrapper.tsx"],"names":[],"mappings":"AA6BA,KAAK,yBAAyB,GAAG;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAGF,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,EAAE,yBAAyB,2CA+S3E"}
@@ -4,12 +4,17 @@
4
4
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
5
  // section: imports
6
6
  import Link from "next/link";
7
- import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger, SidebarInset, } from "../../../ui/sidebar.js";
8
- import { LogIn, UserPlus, BookOpen, ExternalLink, Database, KeyRound, MailCheck, Key, User, ShieldCheck, CircleUserRound, FileJson, Building2, Palette, Settings2, Play } from "lucide-react";
7
+ import { usePathname } from "next/navigation";
8
+ import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarTrigger, SidebarInset, } from "../../../ui/sidebar.js";
9
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
10
+ import { LogIn, UserPlus, BookOpen, ExternalLink, Database, KeyRound, MailCheck, Key, User, ShieldCheck, CircleUserRound, FileJson, Building2, Palette, Settings2, Play, ChevronDown, LayoutGrid } from "lucide-react";
9
11
  import { use_auth_status } from "../hooks/use_auth_status.js";
10
12
  import { ProfilePicMenu } from "./profile_pic_menu.js";
11
13
  // section: component
12
14
  export function SidebarLayoutWrapper({ children }) {
13
15
  const authStatus = use_auth_status();
14
- return (_jsx(SidebarProvider, { children: _jsxs("div", { className: "cls_sidebar_layout_wrapper flex min-h-screen w-full", children: [_jsxs(Sidebar, { children: [_jsx(SidebarHeader, { className: "cls_sidebar_layout_header", children: _jsx("div", { className: "cls_sidebar_layout_title flex items-center gap-2 px-2 py-4", children: _jsx("h1", { className: "cls_sidebar_layout_title_text text-lg font-semibold text-sidebar-foreground", children: "hazo auth" }) }) }), _jsxs(SidebarContent, { className: "cls_sidebar_layout_content", children: [_jsxs(SidebarGroup, { className: "cls_sidebar_layout_test_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Test components" }), _jsxs(SidebarMenu, { className: "cls_sidebar_layout_test_menu", children: [_jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_login_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/login", className: "cls_sidebar_layout_test_login_link flex items-center gap-2", "aria-label": "Test login layout component", children: [_jsx(LogIn, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test login" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_register_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/register", className: "cls_sidebar_layout_test_register_link flex items-center gap-2", "aria-label": "Test register layout component", children: [_jsx(UserPlus, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test register" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_forgot_password_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/forgot_password", className: "cls_sidebar_layout_test_forgot_password_link flex items-center gap-2", "aria-label": "Test forgot password layout component", children: [_jsx(KeyRound, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test forgot password" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_reset_password_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/reset_password", className: "cls_sidebar_layout_test_reset_password_link flex items-center gap-2", "aria-label": "Test reset password layout component", children: [_jsx(Key, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test reset password" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_email_verification_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/verify_email", className: "cls_sidebar_layout_test_email_verification_link flex items-center gap-2", "aria-label": "Test email verification layout component", children: [_jsx(MailCheck, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test email verification" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_sqlite_admin_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_connect/sqlite_admin", className: "cls_sidebar_layout_sqlite_admin_link flex items-center gap-2", "aria-label": "Open SQLite admin UI to browse and edit database", children: [_jsx(Database, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "SQLite Admin" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_user_management_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/user_management", className: "cls_sidebar_layout_user_management_link flex items-center gap-2", "aria-label": "Open User Management to manage users, roles, and permissions", children: [_jsx(User, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "User Management" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_rbac_test_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/rbac_test", className: "cls_sidebar_layout_rbac_test_link flex items-center gap-2", "aria-label": "Test RBAC and HRBAC access control", children: [_jsx(ShieldCheck, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "RBAC/HRBAC Test" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_profile_stamp_test_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/profile_stamp_test", className: "cls_sidebar_layout_profile_stamp_test_link flex items-center gap-2", "aria-label": "Test ProfileStamp component", children: [_jsx(CircleUserRound, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "ProfileStamp Test" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_app_user_data_test_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/app_user_data_test", className: "cls_sidebar_layout_app_user_data_test_link flex items-center gap-2", "aria-label": "Test app_user_data JSON storage", children: [_jsx(FileJson, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "App User Data Test" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_create_firm_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/create_firm", className: "cls_sidebar_layout_create_firm_link flex items-center gap-2", "aria-label": "Test create firm flow", children: [_jsx(Building2, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Create Firm" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_edit_firm_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/edit_firm", className: "cls_sidebar_layout_edit_firm_link flex items-center gap-2", "aria-label": "Test branding editor for firm", children: [_jsx(Palette, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Edit Firm" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_relationships_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/relationships", className: "cls_sidebar_layout_relationships_link flex items-center gap-2", "aria-label": "Test relationship accounts", children: [_jsx(User, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Relationships" })] }) }) })] })] }), _jsxs(SidebarGroup, { className: "cls_sidebar_layout_testing_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Testing" }), _jsx(SidebarMenu, { className: "cls_sidebar_layout_testing_menu", children: _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_scenarios_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/test_scenarios", className: "cls_sidebar_layout_test_scenarios_link flex items-center gap-2", "aria-label": "Test scenarios", children: [_jsx(Settings2, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test Scenarios" })] }) }) }) })] }), _jsxs(SidebarGroup, { className: "cls_sidebar_layout_auto_test_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Auto Tests" }), _jsx(SidebarMenu, { className: "cls_sidebar_layout_auto_test_menu", children: _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_auto_test_runner_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/auto_test", className: "cls_sidebar_layout_auto_test_runner_link flex items-center gap-2", "aria-label": "Run automated tests", children: [_jsx(Play, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Auto Test Runner" })] }) }) }) })] }), _jsx(ProfilePicMenu, { variant: "sidebar", avatar_size: "sm", className: "cls_sidebar_layout_profile_menu", sidebar_group_label: "Account" }), _jsxs(SidebarGroup, { className: "cls_sidebar_layout_resources_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Resources" }), _jsxs(SidebarMenu, { className: "cls_sidebar_layout_resources_menu", children: [_jsx(SidebarMenuItem, { className: "cls_sidebar_layout_storybook_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs("a", { href: "http://localhost:6006", target: "_blank", rel: "noopener noreferrer", className: "cls_sidebar_layout_storybook_link flex items-center gap-2", "aria-label": "Open Storybook preview for reusable components", children: [_jsx(BookOpen, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Storybook" }), _jsx(ExternalLink, { className: "ml-auto h-3 w-3", "aria-hidden": "true" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_docs_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs("a", { href: "https://ui.shadcn.com/docs", target: "_blank", rel: "noopener noreferrer", className: "cls_sidebar_layout_docs_link flex items-center gap-2", "aria-label": "Review shadcn documentation for styling guidance", children: [_jsx(BookOpen, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Shadcn docs" }), _jsx(ExternalLink, { className: "ml-auto h-3 w-3", "aria-hidden": "true" })] }) }) })] })] })] })] }), _jsxs(SidebarInset, { className: "cls_sidebar_layout_inset", children: [_jsxs("header", { className: "cls_sidebar_layout_main_header flex h-16 shrink-0 items-center gap-2 border-b px-4", children: [_jsx(SidebarTrigger, { className: "cls_sidebar_layout_trigger" }), _jsx("div", { className: "cls_sidebar_layout_main_header_content flex flex-1 items-center gap-2", children: _jsx("h2", { className: "cls_sidebar_layout_main_title text-lg font-semibold text-foreground", children: "hazo reusable ui library workspace" }) }), _jsx(ProfilePicMenu, { className: "cls_sidebar_layout_auth_status", avatar_size: "sm" })] }), _jsx("main", { className: "cls_sidebar_layout_main_content flex flex-1 items-center justify-center p-6", children: children })] })] }) }));
16
+ const pathname = usePathname();
17
+ const login_paths = ["/hazo_auth/login", "/hazo_auth/login_variations"];
18
+ const login_submenu_open = login_paths.some((p) => pathname === null || pathname === void 0 ? void 0 : pathname.startsWith(p));
19
+ return (_jsx(SidebarProvider, { children: _jsxs("div", { className: "cls_sidebar_layout_wrapper flex min-h-screen w-full", children: [_jsxs(Sidebar, { children: [_jsx(SidebarHeader, { className: "cls_sidebar_layout_header", children: _jsx("div", { className: "cls_sidebar_layout_title flex items-center gap-2 px-2 py-4", children: _jsx("h1", { className: "cls_sidebar_layout_title_text text-lg font-semibold text-sidebar-foreground", children: "hazo auth" }) }) }), _jsxs(SidebarContent, { className: "cls_sidebar_layout_content", children: [_jsxs(SidebarGroup, { className: "cls_sidebar_layout_test_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Test components" }), _jsxs(SidebarMenu, { className: "cls_sidebar_layout_test_menu", children: [_jsx(SidebarMenuItem, { className: "cls_sidebar_layout_login_group_item", children: _jsxs(Collapsible, { defaultOpen: login_submenu_open, className: "group/login-collapsible w-full", children: [_jsx(CollapsibleTrigger, { asChild: true, children: _jsxs(SidebarMenuButton, { className: "cls_sidebar_layout_login_trigger flex items-center gap-2 w-full", "aria-label": "Toggle login submenu", children: [_jsx(LogIn, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Login" }), _jsx(ChevronDown, { className: "ml-auto h-4 w-4 transition-transform duration-200 group-data-[state=open]/login-collapsible:rotate-180", "aria-hidden": "true" })] }) }), _jsx(CollapsibleContent, { children: _jsxs(SidebarMenuSub, { className: "cls_sidebar_layout_login_submenu", children: [_jsx(SidebarMenuSubItem, { className: "cls_sidebar_layout_login_test_item", children: _jsx(SidebarMenuSubButton, { asChild: true, children: _jsx(Link, { href: "/hazo_auth/login", className: "cls_sidebar_layout_login_test_link flex items-center gap-2", "aria-label": "Test login layout component", children: _jsx("span", { children: "Test login" }) }) }) }), _jsx(SidebarMenuSubItem, { className: "cls_sidebar_layout_login_variations_item", children: _jsx(SidebarMenuSubButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/login_variations", className: "cls_sidebar_layout_login_variations_link flex items-center gap-2", "aria-label": "View login page variations", children: [_jsx(LayoutGrid, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), _jsx("span", { children: "Variations" })] }) }) })] }) })] }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_register_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/register", className: "cls_sidebar_layout_test_register_link flex items-center gap-2", "aria-label": "Test register layout component", children: [_jsx(UserPlus, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test register" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_forgot_password_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/forgot_password", className: "cls_sidebar_layout_test_forgot_password_link flex items-center gap-2", "aria-label": "Test forgot password layout component", children: [_jsx(KeyRound, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test forgot password" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_reset_password_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/reset_password", className: "cls_sidebar_layout_test_reset_password_link flex items-center gap-2", "aria-label": "Test reset password layout component", children: [_jsx(Key, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test reset password" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_email_verification_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/verify_email", className: "cls_sidebar_layout_test_email_verification_link flex items-center gap-2", "aria-label": "Test email verification layout component", children: [_jsx(MailCheck, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test email verification" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_sqlite_admin_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_connect/sqlite_admin", className: "cls_sidebar_layout_sqlite_admin_link flex items-center gap-2", "aria-label": "Open SQLite admin UI to browse and edit database", children: [_jsx(Database, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "SQLite Admin" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_user_management_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/user_management", className: "cls_sidebar_layout_user_management_link flex items-center gap-2", "aria-label": "Open User Management to manage users, roles, and permissions", children: [_jsx(User, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "User Management" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_rbac_test_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/rbac_test", className: "cls_sidebar_layout_rbac_test_link flex items-center gap-2", "aria-label": "Test RBAC and HRBAC access control", children: [_jsx(ShieldCheck, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "RBAC/HRBAC Test" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_profile_stamp_test_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/profile_stamp_test", className: "cls_sidebar_layout_profile_stamp_test_link flex items-center gap-2", "aria-label": "Test ProfileStamp component", children: [_jsx(CircleUserRound, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "ProfileStamp Test" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_app_user_data_test_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/app_user_data_test", className: "cls_sidebar_layout_app_user_data_test_link flex items-center gap-2", "aria-label": "Test app_user_data JSON storage", children: [_jsx(FileJson, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "App User Data Test" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_create_firm_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/create_firm", className: "cls_sidebar_layout_create_firm_link flex items-center gap-2", "aria-label": "Test create firm flow", children: [_jsx(Building2, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Create Firm" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_edit_firm_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/edit_firm", className: "cls_sidebar_layout_edit_firm_link flex items-center gap-2", "aria-label": "Test branding editor for firm", children: [_jsx(Palette, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Edit Firm" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_relationships_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/relationships", className: "cls_sidebar_layout_relationships_link flex items-center gap-2", "aria-label": "Test relationship accounts", children: [_jsx(User, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Relationships" })] }) }) })] })] }), _jsxs(SidebarGroup, { className: "cls_sidebar_layout_testing_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Testing" }), _jsx(SidebarMenu, { className: "cls_sidebar_layout_testing_menu", children: _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_test_scenarios_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/test_scenarios", className: "cls_sidebar_layout_test_scenarios_link flex items-center gap-2", "aria-label": "Test scenarios", children: [_jsx(Settings2, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Test Scenarios" })] }) }) }) })] }), _jsxs(SidebarGroup, { className: "cls_sidebar_layout_auto_test_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Auto Tests" }), _jsx(SidebarMenu, { className: "cls_sidebar_layout_auto_test_menu", children: _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_auto_test_runner_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs(Link, { href: "/hazo_auth/auto_test", className: "cls_sidebar_layout_auto_test_runner_link flex items-center gap-2", "aria-label": "Run automated tests", children: [_jsx(Play, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Auto Test Runner" })] }) }) }) })] }), _jsx(ProfilePicMenu, { variant: "sidebar", avatar_size: "sm", className: "cls_sidebar_layout_profile_menu", sidebar_group_label: "Account" }), _jsxs(SidebarGroup, { className: "cls_sidebar_layout_resources_group", children: [_jsx(SidebarGroupLabel, { className: "cls_sidebar_layout_group_label", children: "Resources" }), _jsxs(SidebarMenu, { className: "cls_sidebar_layout_resources_menu", children: [_jsx(SidebarMenuItem, { className: "cls_sidebar_layout_storybook_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs("a", { href: "http://localhost:6006", target: "_blank", rel: "noopener noreferrer", className: "cls_sidebar_layout_storybook_link flex items-center gap-2", "aria-label": "Open Storybook preview for reusable components", children: [_jsx(BookOpen, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Storybook" }), _jsx(ExternalLink, { className: "ml-auto h-3 w-3", "aria-hidden": "true" })] }) }) }), _jsx(SidebarMenuItem, { className: "cls_sidebar_layout_docs_item", children: _jsx(SidebarMenuButton, { asChild: true, children: _jsxs("a", { href: "https://ui.shadcn.com/docs", target: "_blank", rel: "noopener noreferrer", className: "cls_sidebar_layout_docs_link flex items-center gap-2", "aria-label": "Review shadcn documentation for styling guidance", children: [_jsx(BookOpen, { className: "h-4 w-4", "aria-hidden": "true" }), _jsx("span", { children: "Shadcn docs" }), _jsx(ExternalLink, { className: "ml-auto h-3 w-3", "aria-hidden": "true" })] }) }) })] })] })] })] }), _jsxs(SidebarInset, { className: "cls_sidebar_layout_inset", children: [_jsxs("header", { className: "cls_sidebar_layout_main_header flex h-16 shrink-0 items-center gap-2 border-b px-4", children: [_jsx(SidebarTrigger, { className: "cls_sidebar_layout_trigger" }), _jsx("div", { className: "cls_sidebar_layout_main_header_content flex flex-1 items-center gap-2", children: _jsx("h2", { className: "cls_sidebar_layout_main_title text-lg font-semibold text-foreground", children: "hazo reusable ui library workspace" }) }), _jsx(ProfilePicMenu, { className: "cls_sidebar_layout_auth_status", avatar_size: "sm" })] }), _jsx("main", { className: "cls_sidebar_layout_main_content flex flex-1 items-center justify-center p-6", children: children })] })] }) }));
15
20
  }
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+ export interface OTPRequestFormProps {
3
+ on_sent?: (email: string) => void;
4
+ api_base?: string;
5
+ email_label?: string;
6
+ submit_label?: string;
7
+ pending_label?: string;
8
+ className?: string;
9
+ }
10
+ export declare function OTPRequestForm({ on_sent, api_base, email_label, submit_label, pending_label, className, }: OTPRequestFormProps): React.ReactElement;
11
+ //# sourceMappingURL=OTPRequestForm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OTPRequestForm.d.ts","sourceRoot":"","sources":["../../../src/components/otp/OTPRequestForm.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,QAA2B,EAC3B,WAA6B,EAC7B,YAA0B,EAC1B,aAA0B,EAC1B,SAAS,GACV,EAAE,mBAAmB,GAAG,KAAK,CAAC,YAAY,CAsE1C"}
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Button } from "../ui/button.js";
5
+ import { Input } from "../ui/input.js";
6
+ import { Label } from "../ui/label.js";
7
+ export function OTPRequestForm({ on_sent, api_base = "/api/hazo_auth", email_label = "Email address", submit_label = "Send code", pending_label = "Sending…", className, }) {
8
+ const [email, set_email] = React.useState("");
9
+ const [pending, set_pending] = React.useState(false);
10
+ const [error, set_error] = React.useState(null);
11
+ async function on_submit(ev) {
12
+ var _a;
13
+ ev.preventDefault();
14
+ set_error(null);
15
+ set_pending(true);
16
+ try {
17
+ const res = await fetch(`${api_base}/otp/request`, {
18
+ method: "POST",
19
+ headers: { "content-type": "application/json" },
20
+ body: JSON.stringify({ email }),
21
+ });
22
+ if (res.status === 429) {
23
+ const body = (await res.json().catch(() => ({})));
24
+ const wait = (_a = body.retry_after_seconds) !== null && _a !== void 0 ? _a : 0;
25
+ set_error(`Too many requests. Try again in ${wait}s.`);
26
+ return;
27
+ }
28
+ if (!res.ok) {
29
+ set_error("Could not send code. Please check the email address and try again.");
30
+ return;
31
+ }
32
+ on_sent === null || on_sent === void 0 ? void 0 : on_sent(email);
33
+ }
34
+ catch (_b) {
35
+ set_error("Network error. Please try again.");
36
+ }
37
+ finally {
38
+ set_pending(false);
39
+ }
40
+ }
41
+ return (_jsxs("form", { onSubmit: on_submit, className: className, "aria-label": "Request sign-in code", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "otp-email", children: email_label }), _jsx(Input, { id: "otp-email", name: "email", type: "email", autoComplete: "email", required: true, value: email, onChange: (e) => set_email(e.target.value), disabled: pending })] }), error && (_jsx("p", { role: "alert", className: "mt-3 text-sm text-destructive", children: error })), _jsx(Button, { type: "submit", disabled: pending || email.length === 0, className: "mt-4 w-full", children: pending ? pending_label : submit_label })] }));
42
+ }
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ export interface OTPVerifyFormProps {
3
+ email: string;
4
+ on_success?: (auth: {
5
+ user_id: string;
6
+ email: string;
7
+ }) => void;
8
+ api_base?: string;
9
+ submit_label?: string;
10
+ pending_label?: string;
11
+ resend_label?: string;
12
+ className?: string;
13
+ redirect_url?: string;
14
+ }
15
+ export declare function OTPVerifyForm({ email, on_success, api_base, submit_label, pending_label, resend_label, className, redirect_url, }: OTPVerifyFormProps): React.ReactElement;
16
+ //# sourceMappingURL=OTPVerifyForm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OTPVerifyForm.d.ts","sourceRoot":"","sources":["../../../src/components/otp/OTPVerifyForm.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,UAAU,EACV,QAA2B,EAC3B,YAAuB,EACvB,aAA4B,EAC5B,YAA4B,EAC5B,SAAS,EACT,YAAY,GACb,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CAoIzC"}
@@ -0,0 +1,75 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Button } from "../ui/button.js";
5
+ import { InputOTP, InputOTPGroup, InputOTPSlot } from "../ui/input-otp.js";
6
+ export function OTPVerifyForm({ email, on_success, api_base = "/api/hazo_auth", submit_label = "Verify", pending_label = "Verifying…", resend_label = "Resend code", className, redirect_url, }) {
7
+ const [code, set_code] = React.useState("");
8
+ const [pending, set_pending] = React.useState(false);
9
+ const [error, set_error] = React.useState(null);
10
+ const [resend_state, set_resend_state] = React.useState("idle");
11
+ const [resend_wait, set_resend_wait] = React.useState(0);
12
+ async function on_submit(ev) {
13
+ ev.preventDefault();
14
+ if (code.length !== 6)
15
+ return;
16
+ set_error(null);
17
+ set_pending(true);
18
+ try {
19
+ const res = await fetch(`${api_base}/otp/verify`, {
20
+ method: "POST",
21
+ headers: { "content-type": "application/json" },
22
+ body: JSON.stringify({ email, code }),
23
+ });
24
+ if (!res.ok) {
25
+ set_error("Invalid or expired code.");
26
+ return;
27
+ }
28
+ const body = (await res.json());
29
+ if (body.ok) {
30
+ on_success === null || on_success === void 0 ? void 0 : on_success({ user_id: body.user_id, email: body.email });
31
+ if (redirect_url) {
32
+ window.location.href = redirect_url;
33
+ }
34
+ }
35
+ else {
36
+ set_error("Invalid or expired code.");
37
+ }
38
+ }
39
+ catch (_a) {
40
+ set_error("Network error. Please try again.");
41
+ }
42
+ finally {
43
+ set_pending(false);
44
+ }
45
+ }
46
+ async function on_resend() {
47
+ var _a;
48
+ set_resend_state("sending");
49
+ set_error(null);
50
+ try {
51
+ const res = await fetch(`${api_base}/otp/request`, {
52
+ method: "POST",
53
+ headers: { "content-type": "application/json" },
54
+ body: JSON.stringify({ email }),
55
+ });
56
+ if (res.status === 429) {
57
+ const body = (await res.json().catch(() => ({})));
58
+ set_resend_state("rate_limited");
59
+ set_resend_wait((_a = body.retry_after_seconds) !== null && _a !== void 0 ? _a : 0);
60
+ return;
61
+ }
62
+ if (!res.ok) {
63
+ set_error("Could not resend code.");
64
+ set_resend_state("idle");
65
+ return;
66
+ }
67
+ set_resend_state("sent");
68
+ }
69
+ catch (_b) {
70
+ set_error("Network error.");
71
+ set_resend_state("idle");
72
+ }
73
+ }
74
+ return (_jsxs("form", { onSubmit: on_submit, className: className, "aria-label": "Enter sign-in code", children: [_jsxs("p", { className: "text-sm text-muted-foreground mb-3", children: ["Enter the 6-digit code sent to ", _jsx("strong", { children: email }), "."] }), _jsx(InputOTP, { maxLength: 6, value: code, onChange: (v) => set_code(v), inputMode: "numeric", autoComplete: "one-time-code", disabled: pending, "aria-label": "One-time code", children: _jsxs(InputOTPGroup, { children: [_jsx(InputOTPSlot, { index: 0 }), _jsx(InputOTPSlot, { index: 1 }), _jsx(InputOTPSlot, { index: 2 }), _jsx(InputOTPSlot, { index: 3 }), _jsx(InputOTPSlot, { index: 4 }), _jsx(InputOTPSlot, { index: 5 })] }) }), error && (_jsx("p", { role: "alert", className: "mt-3 text-sm text-destructive", children: error })), _jsx(Button, { type: "submit", disabled: pending || code.length !== 6, className: "mt-4 w-full", children: pending ? pending_label : submit_label }), _jsx("div", { className: "mt-3 text-sm text-center", children: resend_state === "sent" ? (_jsx("span", { className: "text-muted-foreground", children: "Code sent." })) : resend_state === "rate_limited" ? (_jsxs("span", { className: "text-muted-foreground", children: ["Wait ", resend_wait, "s before resending."] })) : (_jsx("button", { type: "button", onClick: on_resend, disabled: resend_state === "sending", className: "underline text-muted-foreground hover:text-foreground", children: resend_label })) })] }));
75
+ }
@@ -0,0 +1,5 @@
1
+ export { OTPRequestForm } from "./OTPRequestForm.js";
2
+ export type { OTPRequestFormProps } from "./OTPRequestForm";
3
+ export { OTPVerifyForm } from "./OTPVerifyForm.js";
4
+ export type { OTPVerifyFormProps } from "./OTPVerifyForm";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/otp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { OTPRequestForm } from "./OTPRequestForm.js";
2
+ export { OTPVerifyForm } from "./OTPVerifyForm.js";
@@ -0,0 +1,35 @@
1
+ import * as React from "react";
2
+ declare const InputOTP: React.ForwardRefExoticComponent<(Omit<Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "onChange" | "maxLength" | "textAlign" | "onComplete" | "pushPasswordManagerStrategy" | "pasteTransformer" | "containerClassName" | "noScriptCSSFallback"> & {
3
+ value?: string;
4
+ onChange?: (newValue: string) => unknown;
5
+ maxLength: number;
6
+ textAlign?: "left" | "center" | "right";
7
+ onComplete?: (...args: any[]) => unknown;
8
+ pushPasswordManagerStrategy?: "increase-width" | "none";
9
+ pasteTransformer?: (pasted: string) => string;
10
+ containerClassName?: string;
11
+ noScriptCSSFallback?: string | null;
12
+ } & {
13
+ render: (props: import("input-otp").RenderProps) => React.ReactNode;
14
+ children?: never;
15
+ } & React.RefAttributes<HTMLInputElement>, "ref"> | Omit<Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "onChange" | "maxLength" | "textAlign" | "onComplete" | "pushPasswordManagerStrategy" | "pasteTransformer" | "containerClassName" | "noScriptCSSFallback"> & {
16
+ value?: string;
17
+ onChange?: (newValue: string) => unknown;
18
+ maxLength: number;
19
+ textAlign?: "left" | "center" | "right";
20
+ onComplete?: (...args: any[]) => unknown;
21
+ pushPasswordManagerStrategy?: "increase-width" | "none";
22
+ pasteTransformer?: (pasted: string) => string;
23
+ containerClassName?: string;
24
+ noScriptCSSFallback?: string | null;
25
+ } & {
26
+ render?: never;
27
+ children: React.ReactNode;
28
+ } & React.RefAttributes<HTMLInputElement>, "ref">) & React.RefAttributes<HTMLInputElement>>;
29
+ declare const InputOTPGroup: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
30
+ declare const InputOTPSlot: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
31
+ index: number;
32
+ } & React.RefAttributes<HTMLDivElement>>;
33
+ declare const InputOTPSeparator: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
34
+ export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
35
+ //# sourceMappingURL=input-otp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-otp.d.ts","sourceRoot":"","sources":["../../../src/components/ui/input-otp.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,QAAA,MAAM,QAAQ;;;;;kBAUoC,GAAG;;;;;;;;;;;;;kBAAH,GAAG;;;;;;;;2FAGnD,CAAC;AAGH,QAAA,MAAM,aAAa,mKAKjB,CAAC;AAGH,QAAA,MAAM,YAAY;WAEiC,MAAM;wCAyBvD,CAAC;AAGH,QAAA,MAAM,iBAAiB,mKAOrB,CAAC;AAGH,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC"}
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
+ import * as React from "react";
15
+ import { OTPInput, OTPInputContext } from "input-otp";
16
+ import { Minus } from "lucide-react";
17
+ import { cn } from "../../lib/utils.js";
18
+ const InputOTP = React.forwardRef((_a, ref) => {
19
+ var { className, containerClassName } = _a, props = __rest(_a, ["className", "containerClassName"]);
20
+ return (_jsx(OTPInput, Object.assign({ ref: ref, containerClassName: cn("flex items-center gap-2 has-[:disabled]:opacity-50", containerClassName), className: cn("disabled:cursor-not-allowed", className) }, props)));
21
+ });
22
+ InputOTP.displayName = "InputOTP";
23
+ const InputOTPGroup = React.forwardRef((_a, ref) => {
24
+ var { className } = _a, props = __rest(_a, ["className"]);
25
+ return (_jsx("div", Object.assign({ ref: ref, className: cn("flex items-center", className) }, props)));
26
+ });
27
+ InputOTPGroup.displayName = "InputOTPGroup";
28
+ const InputOTPSlot = React.forwardRef((_a, ref) => {
29
+ var _b;
30
+ var { index, className } = _a, props = __rest(_a, ["index", "className"]);
31
+ const inputOTPContext = React.useContext(OTPInputContext);
32
+ const slot = (_b = inputOTPContext === null || inputOTPContext === void 0 ? void 0 : inputOTPContext.slots) === null || _b === void 0 ? void 0 : _b[index];
33
+ const char = slot === null || slot === void 0 ? void 0 : slot.char;
34
+ const hasFakeCaret = slot === null || slot === void 0 ? void 0 : slot.hasFakeCaret;
35
+ const isActive = slot === null || slot === void 0 ? void 0 : slot.isActive;
36
+ return (_jsxs("div", Object.assign({ ref: ref, className: cn("relative flex h-10 w-10 items-center justify-center border-y border-r border-input text-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md", isActive && "z-10 ring-2 ring-ring ring-offset-background", className) }, props, { children: [char, hasFakeCaret && (_jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: _jsx("div", { className: "h-4 w-px animate-caret-blink bg-foreground duration-1000" }) }))] })));
37
+ });
38
+ InputOTPSlot.displayName = "InputOTPSlot";
39
+ const InputOTPSeparator = React.forwardRef((_a, ref) => {
40
+ var props = __rest(_a, []);
41
+ return (_jsx("div", Object.assign({ ref: ref, role: "separator" }, props, { children: _jsx(Minus, {}) })));
42
+ });
43
+ InputOTPSeparator.displayName = "InputOTPSeparator";
44
+ export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export * from "./contexts/hazo_auth_provider.js";
2
2
  export * from "./contexts/hazo_auth_config.js";
3
3
  export * from "./components/index.js";
4
- export type { HazoAuthUser, HazoAuthResult, HazoAuthError, HazoAuthOptions, ScopeDetails, TenantOrganization, TenantAuthOptions, TenantAuthResult, RequiredTenantAuthResult, } from "./lib/auth/auth_types";
4
+ export type { HazoAuthUser, HazoAuthResult, HazoAuthError, HazoAuthOptions, ScopeDetails, SelectedScope, TenantAuthOptions, TenantAuthResult, RequiredTenantAuthResult, } from "./lib/auth/auth_types";
5
5
  export { AuthenticationRequiredError, TenantRequiredError, TenantAccessDeniedError, } from "./lib/auth/auth_types.js";
6
6
  export { cn, merge_class_names } from "./lib/utils.js";
7
7
  export { HAZO_AUTH_PERMISSIONS, ALL_ADMIN_PERMISSIONS } from "./lib/constants.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAG5C,cAAc,oBAAoB,CAAC;AAGnC,YAAY,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGpD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAG5C,cAAc,oBAAoB,CAAC;AAGnC,YAAY,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,eAAe,EACf,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGpD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -103,10 +103,11 @@ export type ScopeDetails = {
103
103
  tagline: string | null;
104
104
  };
105
105
  /**
106
- * Tenant/organization information returned in tenant auth results
107
- * Simplified view of scope for API responses
106
+ * Currently selected scope information returned in tenant auth results.
107
+ * Simplified view of the scope chosen via the scope-selection cookie or
108
+ * `X-Hazo-Scope-Id` header.
108
109
  */
109
- export type TenantOrganization = {
110
+ export type SelectedScope = {
110
111
  id: string;
111
112
  name: string;
112
113
  slug: string | null;
@@ -144,9 +145,9 @@ export type TenantAuthResult = {
144
145
  permissions: string[];
145
146
  permission_ok: boolean;
146
147
  missing_permissions?: string[];
147
- organization: TenantOrganization | null;
148
- /** Shorthand for organization?.id - commonly used for DB query filters */
149
- organization_id: string | null;
148
+ selected_scope: SelectedScope | null;
149
+ /** Shorthand for selected_scope?.id - commonly used for DB query filters. */
150
+ selected_scope_id: string | null;
150
151
  user_scopes: ScopeDetails[];
151
152
  scope_ok?: boolean;
152
153
  scope_access_via?: ScopeAccessInfo;
@@ -155,19 +156,19 @@ export type TenantAuthResult = {
155
156
  user: null;
156
157
  permissions: [];
157
158
  permission_ok: false;
158
- organization: null;
159
- /** Shorthand for organization?.id - commonly used for DB query filters */
160
- organization_id: null;
159
+ selected_scope: null;
160
+ /** Shorthand for selected_scope?.id - commonly used for DB query filters. */
161
+ selected_scope_id: null;
161
162
  user_scopes: [];
162
163
  scope_ok?: false;
163
164
  };
164
165
  /**
165
- * Guaranteed authenticated result with non-null organization
166
- * Returned by require_tenant_auth when validation passes
166
+ * Guaranteed authenticated result with non-null selected_scope.
167
+ * Returned by require_tenant_auth when validation passes.
167
168
  */
168
169
  export type RequiredTenantAuthResult = TenantAuthResult & {
169
170
  authenticated: true;
170
- organization: TenantOrganization;
171
+ selected_scope: SelectedScope;
171
172
  };
172
173
  /**
173
174
  * Base error class for all hazo_auth errors