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.
- package/README.md +167 -17
- package/SETUP_CHECKLIST.md +99 -7
- package/cli-src/cli/generate.ts +10 -1
- package/cli-src/cli/validate.ts +4 -0
- package/cli-src/lib/auth/auth_types.ts +21 -12
- package/cli-src/lib/auth/hazo_get_tenant_auth.server.ts +25 -24
- package/cli-src/lib/auth/index.ts +2 -2
- package/cli-src/lib/auth/with_auth.server.ts +15 -15
- package/cli-src/lib/cookies_config.server.ts +1 -0
- package/cli-src/lib/login_config.server.ts +14 -0
- package/cli-src/lib/otp_config.server.ts +91 -0
- package/cli-src/lib/services/email_service.ts +3 -1
- package/cli-src/lib/services/email_template_manifest.ts +17 -0
- package/cli-src/lib/services/email_templates/otp_signin_code.html +13 -0
- package/cli-src/lib/services/email_templates/otp_signin_code.txt +5 -0
- package/cli-src/lib/services/index.ts +8 -2
- package/cli-src/lib/services/otp_service.ts +295 -0
- package/cli-src/lib/services/session_token_service.ts +4 -1
- package/config/hazo_auth_config.example.ini +38 -0
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +10 -1
- package/dist/cli/validate.d.ts.map +1 -1
- package/dist/cli/validate.js +4 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -0
- package/dist/components/layouts/login/index.d.ts +7 -1
- package/dist/components/layouts/login/index.d.ts.map +1 -1
- package/dist/components/layouts/login/index.js +2 -2
- package/dist/components/layouts/otp/index.d.ts +10 -0
- package/dist/components/layouts/otp/index.d.ts.map +1 -0
- package/dist/components/layouts/otp/index.js +14 -0
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +8 -3
- package/dist/components/otp/OTPRequestForm.d.ts +11 -0
- package/dist/components/otp/OTPRequestForm.d.ts.map +1 -0
- package/dist/components/otp/OTPRequestForm.js +42 -0
- package/dist/components/otp/OTPVerifyForm.d.ts +16 -0
- package/dist/components/otp/OTPVerifyForm.d.ts.map +1 -0
- package/dist/components/otp/OTPVerifyForm.js +75 -0
- package/dist/components/otp/index.d.ts +5 -0
- package/dist/components/otp/index.d.ts.map +1 -0
- package/dist/components/otp/index.js +2 -0
- package/dist/components/ui/input-otp.d.ts +35 -0
- package/dist/components/ui/input-otp.d.ts.map +1 -0
- package/dist/components/ui/input-otp.js +44 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.d.ts +13 -12
- package/dist/lib/auth/auth_types.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.js +8 -0
- package/dist/lib/auth/hazo_get_tenant_auth.server.d.ts +8 -7
- package/dist/lib/auth/hazo_get_tenant_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_tenant_auth.server.js +23 -22
- package/dist/lib/auth/index.d.ts +2 -2
- package/dist/lib/auth/index.d.ts.map +1 -1
- package/dist/lib/auth/with_auth.server.d.ts +13 -13
- package/dist/lib/auth/with_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/with_auth.server.js +2 -2
- package/dist/lib/cookies_config.server.d.ts +1 -0
- package/dist/lib/cookies_config.server.d.ts.map +1 -1
- package/dist/lib/cookies_config.server.js +1 -0
- package/dist/lib/login_config.server.d.ts +6 -0
- package/dist/lib/login_config.server.d.ts.map +1 -1
- package/dist/lib/login_config.server.js +7 -0
- package/dist/lib/otp_config.server.d.ts +49 -0
- package/dist/lib/otp_config.server.d.ts.map +1 -0
- package/dist/lib/otp_config.server.js +48 -0
- package/dist/lib/services/email_service.d.ts +1 -1
- package/dist/lib/services/email_service.d.ts.map +1 -1
- package/dist/lib/services/email_service.js +2 -0
- package/dist/lib/services/email_template_manifest.d.ts.map +1 -1
- package/dist/lib/services/email_template_manifest.js +17 -0
- package/dist/lib/services/email_templates/otp_signin_code.html +13 -0
- package/dist/lib/services/email_templates/otp_signin_code.txt +5 -0
- package/dist/lib/services/index.d.ts +2 -0
- package/dist/lib/services/index.d.ts.map +1 -1
- package/dist/lib/services/index.js +1 -0
- package/dist/lib/services/otp_service.d.ts +46 -0
- package/dist/lib/services/otp_service.d.ts.map +1 -0
- package/dist/lib/services/otp_service.js +238 -0
- package/dist/lib/services/session_token_service.d.ts +3 -1
- package/dist/lib/services/session_token_service.d.ts.map +1 -1
- package/dist/lib/services/session_token_service.js +4 -2
- package/dist/page_components/otp.d.ts +4 -0
- package/dist/page_components/otp.d.ts.map +1 -0
- package/dist/page_components/otp.js +5 -0
- package/dist/server/routes/index.d.ts +2 -0
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +3 -0
- package/dist/server/routes/me.d.ts.map +1 -1
- package/dist/server/routes/me.js +43 -1
- package/dist/server/routes/otp/request.d.ts +3 -0
- package/dist/server/routes/otp/request.d.ts.map +1 -0
- package/dist/server/routes/otp/request.js +33 -0
- package/dist/server/routes/otp/verify.d.ts +3 -0
- package/dist/server/routes/otp/verify.d.ts.map +1 -0
- package/dist/server/routes/otp/verify.js +58 -0
- package/dist/server-lib.d.ts +3 -0
- package/dist/server-lib.d.ts.map +1 -1
- package/dist/server-lib.js +2 -0
- package/dist/server_pages/forgot_password.d.ts +1 -1
- package/dist/server_pages/forgot_password.d.ts.map +1 -1
- package/dist/server_pages/forgot_password.js +2 -1
- package/dist/server_pages/login.d.ts +1 -1
- package/dist/server_pages/login.d.ts.map +1 -1
- package/dist/server_pages/login.js +3 -2
- package/dist/server_pages/login_client_wrapper.d.ts +1 -1
- package/dist/server_pages/login_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/login_client_wrapper.js +2 -2
- package/dist/server_pages/my_settings.d.ts +1 -1
- package/dist/server_pages/my_settings.d.ts.map +1 -1
- package/dist/server_pages/my_settings.js +2 -1
- package/dist/server_pages/otp.d.ts +42 -0
- package/dist/server_pages/otp.d.ts.map +1 -0
- package/dist/server_pages/otp.js +38 -0
- package/dist/server_pages/register.d.ts +1 -1
- package/dist/server_pages/register.d.ts.map +1 -1
- package/dist/server_pages/register.js +2 -1
- package/dist/server_pages/reset_password.d.ts +1 -1
- package/dist/server_pages/reset_password.d.ts.map +1 -1
- package/dist/server_pages/reset_password.js +2 -1
- package/dist/server_pages/verify_email.d.ts +1 -1
- package/dist/server_pages/verify_email.d.ts.map +1 -1
- package/dist/server_pages/verify_email.js +2 -1
- package/package.json +20 -3
package/dist/cli/generate.js
CHANGED
|
@@ -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
|
|
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;
|
|
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"}
|
package/dist/cli/validate.js
CHANGED
|
@@ -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";
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAYA,cAAc,oBAAoB,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":"
|
|
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 {
|
|
8
|
-
import {
|
|
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 @@
|
|
|
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,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,
|
|
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";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
-
*
|
|
107
|
-
* Simplified view of scope
|
|
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
|
|
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
|
-
|
|
148
|
-
/** Shorthand for
|
|
149
|
-
|
|
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
|
-
|
|
159
|
-
/** Shorthand for
|
|
160
|
-
|
|
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
|
|
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
|
-
|
|
171
|
+
selected_scope: SelectedScope;
|
|
171
172
|
};
|
|
172
173
|
/**
|
|
173
174
|
* Base error class for all hazo_auth errors
|