hazo_auth 4.1.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +230 -0
- package/SETUP_CHECKLIST.md +202 -0
- package/dist/app/api/hazo_auth/forgot_password/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/forgot_password/route.js +15 -0
- package/dist/app/api/hazo_auth/logout/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/logout/route.js +31 -0
- package/dist/app/api/hazo_auth/me/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/me/route.js +10 -0
- package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.d.ts +2 -0
- package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.d.ts.map +1 -1
- package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.js +8 -0
- package/dist/components/layouts/forgot_password/index.d.ts +7 -1
- package/dist/components/layouts/forgot_password/index.d.ts.map +1 -1
- package/dist/components/layouts/forgot_password/index.js +7 -2
- package/dist/components/layouts/login/index.d.ts +13 -1
- package/dist/components/layouts/login/index.d.ts.map +1 -1
- package/dist/components/layouts/login/index.js +11 -2
- package/dist/components/layouts/my_settings/components/connected_accounts_section.d.ts +17 -0
- package/dist/components/layouts/my_settings/components/connected_accounts_section.d.ts.map +1 -0
- package/dist/components/layouts/my_settings/components/connected_accounts_section.js +17 -0
- package/dist/components/layouts/my_settings/components/set_password_section.d.ts +26 -0
- package/dist/components/layouts/my_settings/components/set_password_section.d.ts.map +1 -0
- package/dist/components/layouts/my_settings/components/set_password_section.js +127 -0
- package/dist/components/layouts/my_settings/hooks/use_my_settings.d.ts +3 -0
- package/dist/components/layouts/my_settings/hooks/use_my_settings.d.ts.map +1 -1
- package/dist/components/layouts/my_settings/hooks/use_my_settings.js +9 -0
- package/dist/components/layouts/my_settings/index.d.ts.map +1 -1
- package/dist/components/layouts/my_settings/index.js +4 -2
- package/dist/components/layouts/shared/components/google_icon.d.ts +12 -0
- package/dist/components/layouts/shared/components/google_icon.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/google_icon.js +9 -0
- package/dist/components/layouts/shared/components/google_sign_in_button.d.ts +21 -0
- package/dist/components/layouts/shared/components/google_sign_in_button.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/google_sign_in_button.js +50 -0
- package/dist/components/layouts/shared/components/oauth_divider.d.ts +13 -0
- package/dist/components/layouts/shared/components/oauth_divider.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/oauth_divider.js +13 -0
- package/dist/components/layouts/shared/hooks/use_auth_status.d.ts +3 -0
- package/dist/components/layouts/shared/hooks/use_auth_status.d.ts.map +1 -1
- package/dist/components/layouts/shared/hooks/use_auth_status.js +4 -0
- package/dist/components/layouts/shared/index.d.ts +5 -0
- package/dist/components/layouts/shared/index.d.ts.map +1 -1
- package/dist/components/layouts/shared/index.js +3 -0
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/lib/auth/nextauth_config.d.ts +34 -0
- package/dist/lib/auth/nextauth_config.d.ts.map +1 -0
- package/dist/lib/auth/nextauth_config.js +171 -0
- package/dist/lib/config/default_config.d.ts +24 -0
- package/dist/lib/config/default_config.d.ts.map +1 -1
- package/dist/lib/config/default_config.js +14 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -0
- package/dist/lib/login_config.server.d.ts +3 -0
- package/dist/lib/login_config.server.d.ts.map +1 -1
- package/dist/lib/login_config.server.js +4 -0
- package/dist/lib/oauth_config.server.d.ts +29 -0
- package/dist/lib/oauth_config.server.d.ts.map +1 -0
- package/dist/lib/oauth_config.server.js +40 -0
- package/dist/lib/services/login_service.d.ts.map +1 -1
- package/dist/lib/services/login_service.js +16 -1
- package/dist/lib/services/oauth_service.d.ts +88 -0
- package/dist/lib/services/oauth_service.d.ts.map +1 -0
- package/dist/lib/services/oauth_service.js +376 -0
- package/dist/lib/services/password_reset_service.d.ts +2 -0
- package/dist/lib/services/password_reset_service.d.ts.map +1 -1
- package/dist/lib/services/password_reset_service.js +10 -0
- package/dist/lib/services/registration_service.d.ts.map +1 -1
- package/dist/lib/services/registration_service.js +1 -0
- package/dist/lib/utils/password_validator.d.ts +13 -0
- package/dist/lib/utils/password_validator.d.ts.map +1 -0
- package/dist/lib/utils/password_validator.js +36 -0
- package/dist/server_pages/login.d.ts.map +1 -1
- package/dist/server_pages/login.js +6 -1
- package/dist/server_pages/login_client_wrapper.d.ts +5 -2
- package/dist/server_pages/login_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/login_client_wrapper.js +2 -2
- package/package.json +2 -1
|
@@ -9,6 +9,7 @@ import { FormHeader } from "../shared/components/form_header";
|
|
|
9
9
|
import { FormActionButtons } from "../shared/components/form_action_buttons";
|
|
10
10
|
import { TwoColumnAuthLayout } from "../shared/components/two_column_auth_layout";
|
|
11
11
|
import { AlreadyLoggedInGuard } from "../shared/components/already_logged_in_guard";
|
|
12
|
+
import { GoogleSignInButton } from "../shared/components/google_sign_in_button";
|
|
12
13
|
import { FORGOT_PASSWORD_FIELD_IDS, createForgotPasswordFieldDefinitions, resolveForgotPasswordButtonPalette, resolveForgotPasswordLabels, } from "./config/forgot_password_field_config";
|
|
13
14
|
import { use_forgot_password_form, } from "./hooks/use_forgot_password_form";
|
|
14
15
|
import Link from "next/link";
|
|
@@ -16,7 +17,7 @@ const ORDERED_FIELDS = [
|
|
|
16
17
|
FORGOT_PASSWORD_FIELD_IDS.EMAIL,
|
|
17
18
|
];
|
|
18
19
|
// section: component
|
|
19
|
-
export default function forgot_password_layout({ image_src, image_alt, image_background_color = "#f1f5f9", field_overrides, labels, button_colors, data_client, sign_in_path = "/hazo_auth/login", sign_in_label = "Sign in", alreadyLoggedInMessage = "You are already logged in", showLogoutButton = true, showReturnHomeButton = false, returnHomeButtonLabel = "Return home", returnHomePath = "/", }) {
|
|
20
|
+
export default function forgot_password_layout({ image_src, image_alt, image_background_color = "#f1f5f9", field_overrides, labels, button_colors, data_client, sign_in_path = "/hazo_auth/login", sign_in_label = "Sign in", alreadyLoggedInMessage = "You are already logged in", showLogoutButton = true, showReturnHomeButton = false, returnHomeButtonLabel = "Return home", returnHomePath = "/", googleOnlyAccountHeading = "Google Account Detected", googleOnlyAccountMessage = "Your account was created using Google Sign-In and doesn't have a password set.", googleOnlyAccountHelpText = "Sign in with Google, then set a password in your account settings if you'd like to use email/password login.", mySettingsPath = "/hazo_auth/my_settings", mySettingsLabel = "Go to Settings", }) {
|
|
20
21
|
const fieldDefinitions = createForgotPasswordFieldDefinitions(field_overrides);
|
|
21
22
|
const resolvedLabels = resolveForgotPasswordLabels(labels);
|
|
22
23
|
const resolvedButtonPalette = resolveForgotPasswordButtonPalette(button_colors);
|
|
@@ -40,5 +41,9 @@ export default function forgot_password_layout({ image_src, image_alt, image_bac
|
|
|
40
41
|
return (_jsx(FormFieldWrapper, { fieldId: fieldDefinition.id, label: fieldDefinition.label, input: inputElement, errorMessage: shouldShowError }, fieldId));
|
|
41
42
|
});
|
|
42
43
|
};
|
|
43
|
-
return (_jsx(AlreadyLoggedInGuard, { image_src: image_src, image_alt: image_alt, image_background_color: image_background_color, message: alreadyLoggedInMessage, showLogoutButton: showLogoutButton, showReturnHomeButton: showReturnHomeButton, returnHomeButtonLabel: returnHomeButtonLabel, returnHomePath: returnHomePath, children: _jsx(TwoColumnAuthLayout, { imageSrc: image_src, imageAlt: image_alt, imageBackgroundColor: image_background_color, formContent:
|
|
44
|
+
return (_jsx(AlreadyLoggedInGuard, { image_src: image_src, image_alt: image_alt, image_background_color: image_background_color, message: alreadyLoggedInMessage, showLogoutButton: showLogoutButton, showReturnHomeButton: showReturnHomeButton, returnHomeButtonLabel: returnHomeButtonLabel, returnHomePath: returnHomePath, children: _jsx(TwoColumnAuthLayout, { imageSrc: image_src, imageAlt: image_alt, imageBackgroundColor: image_background_color, formContent: _jsx(_Fragment, { children: form.isGoogleOnlyAccount ? (
|
|
45
|
+
/* Google-only account message */
|
|
46
|
+
_jsxs("div", { className: "cls_forgot_password_google_only flex flex-col gap-5", children: [_jsx(FormHeader, { heading: googleOnlyAccountHeading, subHeading: googleOnlyAccountMessage }), _jsx("p", { className: "cls_forgot_password_google_only_help text-sm text-slate-600", children: googleOnlyAccountHelpText }), _jsxs("div", { className: "cls_forgot_password_google_only_actions flex flex-col gap-3", children: [_jsx(GoogleSignInButton, { label: "Sign in with Google" }), _jsx(Link, { href: mySettingsPath, className: "cls_forgot_password_settings_link text-center text-sm font-medium text-slate-700 hover:text-slate-900 hover:underline", children: mySettingsLabel })] }), _jsx("div", { className: "cls_forgot_password_sign_in_link mt-2 text-center text-sm text-slate-600", children: _jsxs(Link, { href: sign_in_path, className: "font-medium text-slate-900 hover:underline", children: ["Back to ", sign_in_label] }) })] })) : (
|
|
47
|
+
/* Normal forgot password form */
|
|
48
|
+
_jsxs(_Fragment, { children: [_jsx(FormHeader, { heading: resolvedLabels.heading, subHeading: resolvedLabels.subHeading }), _jsxs("form", { className: "cls_forgot_password_layout_form_fields flex flex-col gap-5", onSubmit: form.handleSubmit, "aria-label": "Forgot password form", children: [renderFields(form), _jsx(FormActionButtons, { submitLabel: resolvedLabels.submitButton, cancelLabel: resolvedLabels.cancelButton, buttonPalette: resolvedButtonPalette, isSubmitDisabled: form.isSubmitDisabled, onCancel: form.handleCancel, submitAriaLabel: "Submit forgot password form", cancelAriaLabel: "Cancel forgot password form" }), form.isSubmitting && (_jsx("div", { className: "cls_forgot_password_submitting_indicator text-sm text-slate-600 text-center", children: "Sending reset link..." }))] }), _jsxs("div", { className: "cls_forgot_password_sign_in_link mt-4 text-center text-sm text-slate-600", children: ["Remember your password?", " ", _jsx(Link, { href: sign_in_path, className: "font-medium text-slate-900 hover:underline", children: sign_in_label })] })] })) }) }) }));
|
|
44
49
|
}
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { StaticImageData } from "next/image";
|
|
2
2
|
import { type ButtonPaletteOverrides, type LayoutFieldMapOverrides, type LayoutLabelOverrides } from "../shared/config/layout_customization";
|
|
3
3
|
import { type LayoutDataClient } from "../shared/data/layout_data_client";
|
|
4
|
+
export type OAuthLayoutConfig = {
|
|
5
|
+
/** Enable Google OAuth login */
|
|
6
|
+
enable_google: boolean;
|
|
7
|
+
/** Enable traditional email/password login */
|
|
8
|
+
enable_email_password: boolean;
|
|
9
|
+
/** Text displayed on the Google sign-in button */
|
|
10
|
+
google_button_text: string;
|
|
11
|
+
/** Text displayed on the divider between OAuth and email/password form */
|
|
12
|
+
oauth_divider_text: string;
|
|
13
|
+
};
|
|
4
14
|
export type LoginLayoutProps<TClient = unknown> = {
|
|
5
15
|
image_src: string | StaticImageData;
|
|
6
16
|
image_alt: string;
|
|
@@ -27,6 +37,8 @@ export type LoginLayoutProps<TClient = unknown> = {
|
|
|
27
37
|
create_account_path?: string;
|
|
28
38
|
create_account_label?: string;
|
|
29
39
|
urlOnLogon?: string;
|
|
40
|
+
/** OAuth configuration */
|
|
41
|
+
oauth?: OAuthLayoutConfig;
|
|
30
42
|
};
|
|
31
|
-
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, urlOnLogon, }: LoginLayoutProps<TClient>): import("react/jsx-runtime").JSX.Element;
|
|
43
|
+
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, urlOnLogon, oauth, }: LoginLayoutProps<TClient>): import("react/jsx-runtime").JSX.Element;
|
|
32
44
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/login/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/login/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAWlD,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EAC1B,MAAM,uCAAuC,CAAC;AAW/C,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAG1E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gCAAgC;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,8CAA8C;IAC9C,qBAAqB,EAAE,OAAO,CAAC;IAC/B,kDAAkD;IAClD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0EAA0E;IAC1E,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,OAAO,GAAG,OAAO,IAAI;IAChD,SAAS,EAAE,MAAM,GAAG,eAAe,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAC1C,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,WAAW,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,CAAC,EAAE;QACP,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QAChE,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QACjE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QAChE,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;KAClE,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,KAAK,CAAC,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AAUF,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,OAAO,EAAE,EAC5C,SAAS,EACT,SAAS,EACT,sBAAkC,EAClC,eAAe,EACf,MAAM,EACN,aAAa,EACb,WAAW,EACX,MAAM,EACN,aAAa,EACb,cAAyC,EACzC,sBAAoD,EACpD,gBAAuB,EACvB,oBAA4B,EAC5B,qBAAqC,EACrC,cAAoB,EACpB,oBAAmD,EACnD,qBAA0C,EAC1C,mBAA2C,EAC3C,oBAAuC,EACvC,UAAU,EACV,KAAK,GACN,EAAE,gBAAgB,CAAC,OAAO,CAAC,2CAuM3B"}
|
|
@@ -12,6 +12,8 @@ import { FormActionButtons } from "../shared/components/form_action_buttons";
|
|
|
12
12
|
import { TwoColumnAuthLayout } from "../shared/components/two_column_auth_layout";
|
|
13
13
|
import { CheckCircle } from "lucide-react";
|
|
14
14
|
import { AlreadyLoggedInGuard } from "../shared/components/already_logged_in_guard";
|
|
15
|
+
import { GoogleSignInButton } from "../shared/components/google_sign_in_button";
|
|
16
|
+
import { OAuthDivider } from "../shared/components/oauth_divider";
|
|
15
17
|
import { LOGIN_FIELD_IDS, createLoginFieldDefinitions, resolveLoginButtonPalette, resolveLoginLabels, } from "./config/login_field_config";
|
|
16
18
|
import { use_login_form, } from "./hooks/use_login_form";
|
|
17
19
|
const ORDERED_FIELDS = [
|
|
@@ -19,7 +21,14 @@ const ORDERED_FIELDS = [
|
|
|
19
21
|
LOGIN_FIELD_IDS.PASSWORD,
|
|
20
22
|
];
|
|
21
23
|
// section: component
|
|
22
|
-
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", urlOnLogon, }) {
|
|
24
|
+
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", urlOnLogon, oauth, }) {
|
|
25
|
+
// Default OAuth config: both enabled
|
|
26
|
+
const oauthConfig = oauth || {
|
|
27
|
+
enable_google: true,
|
|
28
|
+
enable_email_password: true,
|
|
29
|
+
google_button_text: "Continue with Google",
|
|
30
|
+
oauth_divider_text: "or continue with email",
|
|
31
|
+
};
|
|
23
32
|
const fieldDefinitions = createLoginFieldDefinitions(field_overrides);
|
|
24
33
|
const resolvedLabels = resolveLoginLabels(labels);
|
|
25
34
|
const resolvedButtonPalette = resolveLoginButtonPalette(button_colors);
|
|
@@ -54,5 +63,5 @@ export default function login_layout({ image_src, image_alt, image_background_co
|
|
|
54
63
|
if (form.isSuccess) {
|
|
55
64
|
return (_jsx(TwoColumnAuthLayout, { imageSrc: image_src, imageAlt: image_alt, imageBackgroundColor: image_background_color, formContent: _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 })] })] }) }));
|
|
56
65
|
}
|
|
57
|
-
return (_jsx(AlreadyLoggedInGuard, { image_src: image_src, image_alt: image_alt, image_background_color: image_background_color, message: alreadyLoggedInMessage, showLogoutButton: showLogoutButton, showReturnHomeButton: showReturnHomeButton, returnHomeButtonLabel: returnHomeButtonLabel, returnHomePath: returnHomePath, children: _jsx(TwoColumnAuthLayout, { imageSrc: image_src, imageAlt: image_alt, imageBackgroundColor: image_background_color, formContent: _jsxs(_Fragment, { children: [_jsx(FormHeader, { heading: resolvedLabels.heading, subHeading: resolvedLabels.subHeading }), _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" }), _jsxs("div", { className: "cls_login_layout_support_links flex flex-col gap-1 text-sm text-muted-foreground", children: [_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 }), _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 })] })] })] }) }) }));
|
|
66
|
+
return (_jsx(AlreadyLoggedInGuard, { image_src: image_src, image_alt: image_alt, image_background_color: image_background_color, message: alreadyLoggedInMessage, showLogoutButton: showLogoutButton, showReturnHomeButton: showReturnHomeButton, returnHomeButtonLabel: returnHomeButtonLabel, returnHomePath: returnHomePath, children: _jsx(TwoColumnAuthLayout, { imageSrc: image_src, imageAlt: image_alt, imageBackgroundColor: image_background_color, formContent: _jsxs(_Fragment, { children: [_jsx(FormHeader, { heading: resolvedLabels.heading, subHeading: resolvedLabels.subHeading }), oauthConfig.enable_google && (_jsx("div", { className: "cls_login_layout_oauth_section", children: _jsx(GoogleSignInButton, { label: oauthConfig.google_button_text }) })), 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" }), _jsxs("div", { className: "cls_login_layout_support_links flex flex-col gap-1 text-sm text-muted-foreground", children: [_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 }), _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 })] })] })), !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 }) }))] }) }) }));
|
|
58
67
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type ConnectedAccountsSectionProps = {
|
|
2
|
+
/** Whether Google account is connected */
|
|
3
|
+
googleConnected: boolean;
|
|
4
|
+
/** User's email address (shown when Google is connected) */
|
|
5
|
+
email?: string;
|
|
6
|
+
/** Section heading */
|
|
7
|
+
heading?: string;
|
|
8
|
+
/** Loading state */
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Connected Accounts Section for My Settings
|
|
13
|
+
* Shows which OAuth providers are linked to the user's account
|
|
14
|
+
* Currently supports Google, designed for future extensibility
|
|
15
|
+
*/
|
|
16
|
+
export declare function ConnectedAccountsSection({ googleConnected, email, heading, loading, }: ConnectedAccountsSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
//# sourceMappingURL=connected_accounts_section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connected_accounts_section.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/my_settings/components/connected_accounts_section.tsx"],"names":[],"mappings":"AAQA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,0CAA0C;IAC1C,eAAe,EAAE,OAAO,CAAC;IACzB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAGF;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,eAAe,EACf,KAAK,EACL,OAA8B,EAC9B,OAAe,GAChB,EAAE,6BAA6B,2CAkD/B"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// file_description: Connected accounts section showing linked OAuth providers
|
|
2
|
+
// section: client_directive
|
|
3
|
+
"use client";
|
|
4
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
|
+
// section: imports
|
|
6
|
+
import { GoogleIcon } from "../../shared/components/google_icon";
|
|
7
|
+
// section: component
|
|
8
|
+
/**
|
|
9
|
+
* Connected Accounts Section for My Settings
|
|
10
|
+
* Shows which OAuth providers are linked to the user's account
|
|
11
|
+
* Currently supports Google, designed for future extensibility
|
|
12
|
+
*/
|
|
13
|
+
export function ConnectedAccountsSection({ googleConnected, email, heading = "Connected Accounts", loading = false, }) {
|
|
14
|
+
return (_jsxs("div", { className: "cls_my_settings_connected_accounts_section bg-white rounded-lg border border-[var(--hazo-border)] p-6", children: [_jsx("h2", { className: "cls_my_settings_section_heading text-lg font-semibold text-[var(--hazo-text-primary)] mb-4", children: heading }), _jsx("div", { className: "cls_connected_accounts_list flex flex-col gap-3", children: _jsxs("div", { className: "cls_connected_account_item flex items-center justify-between p-3 rounded-md border border-[var(--hazo-border)] bg-[var(--hazo-surface-muted)]", children: [_jsxs("div", { className: "cls_connected_account_info flex items-center gap-3", children: [_jsx("div", { className: "cls_connected_account_icon flex items-center justify-center w-10 h-10 rounded-full bg-white border border-[var(--hazo-border)]", children: _jsx(GoogleIcon, { width: 20, height: 20 }) }), _jsxs("div", { className: "cls_connected_account_details flex flex-col", children: [_jsx("span", { className: "cls_connected_account_name font-medium text-[var(--hazo-text-primary)]", children: "Google" }), googleConnected && email && (_jsx("span", { className: "cls_connected_account_email text-sm text-[var(--hazo-text-muted)]", children: email }))] })] }), _jsx("div", { className: "cls_connected_account_status", children: loading ? (_jsx("span", { className: "cls_connected_account_loading text-sm text-[var(--hazo-text-muted)] animate-pulse", children: "Loading..." })) : googleConnected ? (_jsxs("span", { className: "cls_connected_account_connected inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-green-100 text-green-700", children: [_jsx("span", { className: "cls_connected_indicator w-1.5 h-1.5 rounded-full bg-green-500" }), "Connected"] })) : (_jsxs("span", { className: "cls_connected_account_not_connected inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-slate-100 text-slate-600", children: [_jsx("span", { className: "cls_not_connected_indicator w-1.5 h-1.5 rounded-full bg-slate-400" }), "Not connected"] })) })] }) }), _jsx("p", { className: "cls_connected_accounts_help text-sm text-[var(--hazo-text-muted)] mt-4", children: googleConnected
|
|
15
|
+
? "Your Google account is linked. You can sign in using either your password or Google."
|
|
16
|
+
: "Link your Google account to enable quick sign-in with Google." })] }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PasswordRequirementOptions } from "../../shared/config/layout_customization";
|
|
2
|
+
export type SetPasswordSectionProps = {
|
|
3
|
+
/** Password requirements for validation */
|
|
4
|
+
passwordRequirements: PasswordRequirementOptions;
|
|
5
|
+
/** Callback when password is successfully set */
|
|
6
|
+
onPasswordSet?: () => void;
|
|
7
|
+
/** Section heading */
|
|
8
|
+
heading?: string;
|
|
9
|
+
/** Description text */
|
|
10
|
+
description?: string;
|
|
11
|
+
/** New password label */
|
|
12
|
+
newPasswordLabel?: string;
|
|
13
|
+
/** Confirm password label */
|
|
14
|
+
confirmPasswordLabel?: string;
|
|
15
|
+
/** Submit button label */
|
|
16
|
+
submitButtonLabel?: string;
|
|
17
|
+
/** Loading state from parent */
|
|
18
|
+
parentLoading?: boolean;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Set Password Section for My Settings
|
|
22
|
+
* Allows Google-only users to set a password for email/password login
|
|
23
|
+
* Only shown when user has no password set (Google-only account)
|
|
24
|
+
*/
|
|
25
|
+
export declare function SetPasswordSection({ passwordRequirements, onPasswordSet, heading, description, newPasswordLabel, confirmPasswordLabel, submitButtonLabel, parentLoading, }: SetPasswordSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
//# sourceMappingURL=set_password_section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set_password_section.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/my_settings/components/set_password_section.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AAI3F,MAAM,MAAM,uBAAuB,GAAG;IACpC,2CAA2C;IAC3C,oBAAoB,EAAE,0BAA0B,CAAC;IACjD,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yBAAyB;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6BAA6B;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,0BAA0B;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gCAAgC;IAChC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAGF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,oBAAoB,EACpB,aAAa,EACb,OAAwB,EACxB,WAA4G,EAC5G,gBAAiC,EACjC,oBAAyC,EACzC,iBAAkC,EAClC,aAAqB,GACtB,EAAE,uBAAuB,2CAoNzB"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// file_description: Set password section for Google-only users to add email/password login
|
|
2
|
+
// section: client_directive
|
|
3
|
+
"use client";
|
|
4
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
|
+
// section: imports
|
|
6
|
+
import { useState, useCallback } from "react";
|
|
7
|
+
import { Button } from "../../../ui/button";
|
|
8
|
+
import { PasswordField } from "../../shared/components/password_field";
|
|
9
|
+
import { FormFieldWrapper } from "../../shared/components/form_field_wrapper";
|
|
10
|
+
import { toast } from "sonner";
|
|
11
|
+
import { useHazoAuthConfig } from "../../../../contexts/hazo_auth_provider";
|
|
12
|
+
// section: component
|
|
13
|
+
/**
|
|
14
|
+
* Set Password Section for My Settings
|
|
15
|
+
* Allows Google-only users to set a password for email/password login
|
|
16
|
+
* Only shown when user has no password set (Google-only account)
|
|
17
|
+
*/
|
|
18
|
+
export function SetPasswordSection({ passwordRequirements, onPasswordSet, heading = "Set Password", description = "Add a password to your account to enable email/password login in addition to Google sign-in.", newPasswordLabel = "New Password", confirmPasswordLabel = "Confirm Password", submitButtonLabel = "Set Password", parentLoading = false, }) {
|
|
19
|
+
const { apiBasePath } = useHazoAuthConfig();
|
|
20
|
+
const [newPassword, setNewPassword] = useState("");
|
|
21
|
+
const [confirmPassword, setConfirmPassword] = useState("");
|
|
22
|
+
const [newPasswordVisible, setNewPasswordVisible] = useState(false);
|
|
23
|
+
const [confirmPasswordVisible, setConfirmPasswordVisible] = useState(false);
|
|
24
|
+
const [errors, setErrors] = useState({});
|
|
25
|
+
const [loading, setLoading] = useState(false);
|
|
26
|
+
/**
|
|
27
|
+
* Validates password against requirements
|
|
28
|
+
*/
|
|
29
|
+
const validatePassword = useCallback((password) => {
|
|
30
|
+
if (!password || password.length < passwordRequirements.minimum_length) {
|
|
31
|
+
return `Password must be at least ${passwordRequirements.minimum_length} characters long`;
|
|
32
|
+
}
|
|
33
|
+
const missingRequirements = [];
|
|
34
|
+
if (passwordRequirements.require_uppercase && !/[A-Z]/.test(password)) {
|
|
35
|
+
missingRequirements.push("uppercase letter");
|
|
36
|
+
}
|
|
37
|
+
if (passwordRequirements.require_lowercase && !/[a-z]/.test(password)) {
|
|
38
|
+
missingRequirements.push("lowercase letter");
|
|
39
|
+
}
|
|
40
|
+
if (passwordRequirements.require_number && !/[0-9]/.test(password)) {
|
|
41
|
+
missingRequirements.push("number");
|
|
42
|
+
}
|
|
43
|
+
if (passwordRequirements.require_special &&
|
|
44
|
+
!/[^A-Za-z0-9]/.test(password)) {
|
|
45
|
+
missingRequirements.push("special character");
|
|
46
|
+
}
|
|
47
|
+
if (missingRequirements.length > 0) {
|
|
48
|
+
return `Password must contain at least one: ${missingRequirements.join(", ")}`;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}, [passwordRequirements]);
|
|
52
|
+
/**
|
|
53
|
+
* Validates the form before submission
|
|
54
|
+
*/
|
|
55
|
+
const validateForm = useCallback(() => {
|
|
56
|
+
const newErrors = {};
|
|
57
|
+
const passwordError = validatePassword(newPassword);
|
|
58
|
+
if (passwordError) {
|
|
59
|
+
newErrors.newPassword = passwordError;
|
|
60
|
+
}
|
|
61
|
+
if (!confirmPassword) {
|
|
62
|
+
newErrors.confirmPassword = "Please confirm your new password";
|
|
63
|
+
}
|
|
64
|
+
else if (newPassword !== confirmPassword) {
|
|
65
|
+
newErrors.confirmPassword = "Passwords do not match";
|
|
66
|
+
}
|
|
67
|
+
setErrors(newErrors);
|
|
68
|
+
return Object.keys(newErrors).length === 0;
|
|
69
|
+
}, [newPassword, confirmPassword, validatePassword]);
|
|
70
|
+
/**
|
|
71
|
+
* Handles form submission
|
|
72
|
+
*/
|
|
73
|
+
const handleSubmit = useCallback(async () => {
|
|
74
|
+
if (!validateForm()) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
setLoading(true);
|
|
78
|
+
try {
|
|
79
|
+
const response = await fetch(`${apiBasePath}/set_password`, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: {
|
|
82
|
+
"Content-Type": "application/json",
|
|
83
|
+
},
|
|
84
|
+
credentials: "include",
|
|
85
|
+
body: JSON.stringify({
|
|
86
|
+
new_password: newPassword,
|
|
87
|
+
}),
|
|
88
|
+
});
|
|
89
|
+
const data = await response.json();
|
|
90
|
+
if (!response.ok || !data.success) {
|
|
91
|
+
const errorMessage = data.error || "Failed to set password";
|
|
92
|
+
toast.error(errorMessage);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
toast.success("Password set successfully! You can now sign in with your email and password.");
|
|
96
|
+
// Reset form
|
|
97
|
+
setNewPassword("");
|
|
98
|
+
setConfirmPassword("");
|
|
99
|
+
setErrors({});
|
|
100
|
+
// Notify parent
|
|
101
|
+
onPasswordSet === null || onPasswordSet === void 0 ? void 0 : onPasswordSet();
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
const errorMessage = error instanceof Error ? error.message : "Failed to set password";
|
|
105
|
+
toast.error(errorMessage);
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
setLoading(false);
|
|
109
|
+
}
|
|
110
|
+
}, [validateForm, newPassword, apiBasePath, onPasswordSet]);
|
|
111
|
+
/**
|
|
112
|
+
* Handles new password field change
|
|
113
|
+
*/
|
|
114
|
+
const handleNewPasswordChange = useCallback((value) => {
|
|
115
|
+
setNewPassword(value);
|
|
116
|
+
setErrors((prev) => (Object.assign(Object.assign({}, prev), { newPassword: undefined })));
|
|
117
|
+
}, []);
|
|
118
|
+
/**
|
|
119
|
+
* Handles confirm password field change
|
|
120
|
+
*/
|
|
121
|
+
const handleConfirmPasswordChange = useCallback((value) => {
|
|
122
|
+
setConfirmPassword(value);
|
|
123
|
+
setErrors((prev) => (Object.assign(Object.assign({}, prev), { confirmPassword: undefined })));
|
|
124
|
+
}, []);
|
|
125
|
+
const isSubmitDisabled = loading || parentLoading || !newPassword || !confirmPassword;
|
|
126
|
+
return (_jsxs("div", { className: "cls_my_settings_set_password_section bg-white rounded-lg border border-[var(--hazo-border)] p-6", children: [_jsx("h2", { className: "cls_my_settings_section_heading text-lg font-semibold text-[var(--hazo-text-primary)] mb-2", children: heading }), _jsx("p", { className: "cls_set_password_description text-sm text-[var(--hazo-text-muted)] mb-4", children: description }), _jsxs("div", { className: "cls_set_password_fields flex flex-col gap-4", children: [_jsxs("div", { className: "cls_set_password_fields_row grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(FormFieldWrapper, { fieldId: "set-new-password", label: newPasswordLabel, input: _jsx(PasswordField, { inputId: "set-new-password", ariaLabel: newPasswordLabel, value: newPassword, placeholder: "Enter your new password", autoComplete: "new-password", isVisible: newPasswordVisible, onChange: handleNewPasswordChange, onToggleVisibility: () => setNewPasswordVisible(!newPasswordVisible), errorMessage: errors.newPassword }) }), _jsx(FormFieldWrapper, { fieldId: "set-confirm-password", label: confirmPasswordLabel, input: _jsx(PasswordField, { inputId: "set-confirm-password", ariaLabel: confirmPasswordLabel, value: confirmPassword, placeholder: "Confirm your new password", autoComplete: "new-password", isVisible: confirmPasswordVisible, onChange: handleConfirmPasswordChange, onToggleVisibility: () => setConfirmPasswordVisible(!confirmPasswordVisible), errorMessage: errors.confirmPassword }) })] }), _jsx("div", { className: "cls_set_password_actions flex justify-end", children: _jsx(Button, { type: "button", onClick: handleSubmit, disabled: isSubmitDisabled, className: "cls_set_password_submit_button", "aria-label": submitButtonLabel, children: loading ? "Setting password..." : submitButtonLabel }) })] })] }));
|
|
127
|
+
}
|
|
@@ -19,6 +19,9 @@ export type UseMySettingsResult = {
|
|
|
19
19
|
profileSource?: "upload" | "library" | "gravatar" | "custom";
|
|
20
20
|
lastLogon?: string;
|
|
21
21
|
loading: boolean;
|
|
22
|
+
authProviders?: string;
|
|
23
|
+
hasPassword?: boolean;
|
|
24
|
+
googleConnected?: boolean;
|
|
22
25
|
passwordFields?: PasswordFields;
|
|
23
26
|
handlePasswordFieldChange: (field: "currentPassword" | "newPassword" | "confirmPassword", value: string) => void;
|
|
24
27
|
togglePasswordVisibility: (field: "currentPassword" | "newPassword" | "confirmPassword") => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use_my_settings.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/my_settings/hooks/use_my_settings.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AAI3F,MAAM,MAAM,cAAc,GAAG;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,OAAO,CAAC;IAChC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,sBAAsB,EAAE,OAAO,CAAC;IAChC,MAAM,EAAE;QACN,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAChC,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAEhC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IAGjB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,yBAAyB,EAAE,CAAC,KAAK,EAAE,iBAAiB,GAAG,aAAa,GAAG,iBAAiB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjH,wBAAwB,EAAE,CAAC,KAAK,EAAE,iBAAiB,GAAG,aAAa,GAAG,iBAAiB,KAAK,IAAI,CAAC;IACjG,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,sBAAsB,EAAE,OAAO,CAAC;IAGhC,wBAAwB,EAAE,OAAO,CAAC;IAClC,wBAAwB,EAAE,MAAM,IAAI,CAAC;IACrC,+BAA+B,EAAE,MAAM,IAAI,CAAC;IAC5C,wBAAwB,EAAE,CAAC,iBAAiB,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzH,0BAA0B,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAGhD,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGlD,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,oBAAoB,EAAE,0BAA0B,CAAC;CAClD,CAAC;AA4BF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAC9B,oBAAoB,GACrB,EAAE,mBAAmB,GAAG,mBAAmB,
|
|
1
|
+
{"version":3,"file":"use_my_settings.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/my_settings/hooks/use_my_settings.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AAI3F,MAAM,MAAM,cAAc,GAAG;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,OAAO,CAAC;IAChC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,sBAAsB,EAAE,OAAO,CAAC;IAChC,MAAM,EAAE;QACN,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAChC,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAEhC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IAGjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAG1B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,yBAAyB,EAAE,CAAC,KAAK,EAAE,iBAAiB,GAAG,aAAa,GAAG,iBAAiB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjH,wBAAwB,EAAE,CAAC,KAAK,EAAE,iBAAiB,GAAG,aAAa,GAAG,iBAAiB,KAAK,IAAI,CAAC;IACjG,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,sBAAsB,EAAE,OAAO,CAAC;IAGhC,wBAAwB,EAAE,OAAO,CAAC;IAClC,wBAAwB,EAAE,MAAM,IAAI,CAAC;IACrC,+BAA+B,EAAE,MAAM,IAAI,CAAC;IAC5C,wBAAwB,EAAE,CAAC,iBAAiB,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzH,0BAA0B,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAGhD,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGlD,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,oBAAoB,EAAE,0BAA0B,CAAC;CAClD,CAAC;AA4BF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAC9B,oBAAoB,GACrB,EAAE,mBAAmB,GAAG,mBAAmB,CAqX3C"}
|
|
@@ -56,6 +56,10 @@ export function use_my_settings({ passwordRequirements, }) {
|
|
|
56
56
|
const profileSource = authStatus.profile_source;
|
|
57
57
|
const lastLogon = authStatus.last_logon;
|
|
58
58
|
const loading = authStatus.loading;
|
|
59
|
+
// OAuth-related data
|
|
60
|
+
const authProviders = authStatus.auth_providers;
|
|
61
|
+
const hasPassword = authStatus.has_password;
|
|
62
|
+
const googleConnected = authStatus.google_connected;
|
|
59
63
|
/**
|
|
60
64
|
* Refreshes user data by triggering auth status refresh
|
|
61
65
|
*/
|
|
@@ -339,6 +343,11 @@ export function use_my_settings({ passwordRequirements, }) {
|
|
|
339
343
|
profileSource,
|
|
340
344
|
lastLogon,
|
|
341
345
|
loading,
|
|
346
|
+
// OAuth-related data
|
|
347
|
+
authProviders,
|
|
348
|
+
hasPassword,
|
|
349
|
+
googleConnected,
|
|
350
|
+
// Password fields
|
|
342
351
|
passwordFields,
|
|
343
352
|
handlePasswordFieldChange,
|
|
344
353
|
togglePasswordVisibility,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/my_settings/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/my_settings/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EACV,0BAA0B,EAC1B,sBAAsB,EACvB,MAAM,uCAAuC,CAAC;AAQ/C,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,wBAAwB,CAAC;IAClC,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,qBAAqB,EAAE,0BAA0B,CAAC;IAClD,cAAc,EAAE;QACd,kBAAkB,EAAE,OAAO,CAAC;QAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,cAAc,EAAE,MAAM,CAAC;QACvB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,4BAA4B,EAAE,UAAU,GAAG,SAAS,CAAC;QACrD,4BAA4B,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;QACtD,kBAAkB,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,UAAU,EAAE;QACV,eAAe,EAAE,OAAO,CAAC;QACzB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,mBAAmB,EAAE,OAAO,CAAC;KAC9B,CAAC;IACF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,EAAE;QACR,6BAA6B,EAAE,MAAM,CAAC;QACtC,sBAAsB,EAAE,MAAM,CAAC;QAC/B,2BAA2B,EAAE,MAAM,CAAC;QACpC,uBAAuB,EAAE,MAAM,CAAC;KACjC,CAAC;IACF,OAAO,EAAE;QACP,aAAa,EAAE,MAAM,CAAC;QACtB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,yBAAyB,EAAE,MAAM,CAAC;QAClC,uBAAuB,EAAE,MAAM,CAAC;QAChC,0BAA0B,EAAE,MAAM,CAAC;QACnC,0BAA0B,EAAE,MAAM,CAAC;QACnC,+BAA+B,EAAE,MAAM,CAAC;QACxC,4BAA4B,EAAE,MAAM,CAAC;KACtC,CAAC;IACF,SAAS,EAAE;QACT,wBAAwB,EAAE,MAAM,EAAE,CAAC;QACnC,wBAAwB,EAAE,MAAM,EAAE,CAAC;KACpC,CAAC;CACH,CAAC;AAGF;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,EACzC,SAAS,EACT,MAAM,EACN,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,UAAU,EACV,mBAAkE,EAClE,gBAAgC,EAChC,SAA8B,EAC9B,OAA4B,EAC5B,UAAoE,EACpE,iBAAmC,EACnC,0BAAqE,EACrE,sBAA2C,EAC3C,sBAAiC,EACjC,uBAA+C,EAC/C,aAA0B,EAC1B,oBAAyC,EACzC,gBAAiC,EACjC,oBAAyC,EACzC,QAAQ,EACR,OAAO,EACP,SAAS,GACV,EAAE,qBAAqB,2CAqOvB"}
|
|
@@ -7,6 +7,8 @@ import { Button } from "../../ui/button";
|
|
|
7
7
|
import { EditableField } from "./components/editable_field";
|
|
8
8
|
import { ProfilePictureDisplay } from "./components/profile_picture_display";
|
|
9
9
|
import { ProfilePictureDialog } from "./components/profile_picture_dialog";
|
|
10
|
+
import { ConnectedAccountsSection } from "./components/connected_accounts_section";
|
|
11
|
+
import { SetPasswordSection } from "./components/set_password_section";
|
|
10
12
|
import { UnauthorizedGuard } from "../shared/components/unauthorized_guard";
|
|
11
13
|
import { use_my_settings } from "./hooks/use_my_settings";
|
|
12
14
|
import { resolveMySettingsLabels, resolveMySettingsButtonPalette, } from "./config/my_settings_field_config";
|
|
@@ -29,10 +31,10 @@ export default function my_settings_layout({ className, labels, button_colors, p
|
|
|
29
31
|
const settings = use_my_settings({
|
|
30
32
|
passwordRequirements: password_requirements,
|
|
31
33
|
});
|
|
32
|
-
return (_jsx(UnauthorizedGuard, { message: unauthorizedMessage, loginButtonLabel: loginButtonLabel, loginPath: loginPath, children: _jsxs("div", { className: cn("cls_my_settings_layout flex flex-col gap-6 p-6 w-full", className), children: [_jsxs("div", { className: "cls_my_settings_layout_header flex flex-col gap-2", children: [_jsx("h1", { className: "cls_my_settings_layout_heading text-3xl font-bold text-[var(--hazo-text-primary)]", children: heading }), _jsx("p", { className: "cls_my_settings_layout_subheading text-[var(--hazo-text-muted)]", children: subHeading })] }), _jsxs("div", { className: "cls_my_settings_layout_profile_photo_section bg-white rounded-lg border border-[var(--hazo-border)] p-6", children: [_jsx("h2", { className: "cls_my_settings_layout_section_heading text-lg font-semibold text-[var(--hazo-text-primary)] mb-4", children: profilePhotoLabel }), _jsx("div", { className: "cls_my_settings_layout_profile_photo_content flex flex-col items-center", children: _jsxs("div", { className: "cls_my_settings_layout_profile_photo_display relative", children: [_jsx(ProfilePictureDisplay, { profilePictureUrl: settings.profilePictureUrl, name: settings.name, email: settings.email, onEdit: settings.handleProfilePictureEdit }), _jsxs("div", { className: "cls_my_settings_layout_profile_photo_actions absolute left-0 right-0 flex items-center justify-between px-2", style: { bottom: '-20px' }, children: [_jsx(Button, { type: "button", onClick: settings.handleProfilePictureEdit, disabled: settings.loading, variant: "ghost", size: "icon", className: "cls_my_settings_layout_upload_photo_button", "aria-label": uploadPhotoButtonLabel, children: _jsx(Pencil, { className: "h-4 w-4", "aria-hidden": "true" }) }), _jsx(Button, { type: "button", onClick: settings.handleProfilePictureRemove, disabled: settings.loading || !settings.profilePictureUrl, variant: "ghost", size: "icon", className: "cls_my_settings_layout_remove_photo_button text-red-600 hover:text-red-700 hover:bg-red-50", "aria-label": removePhotoButtonLabel, children: _jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" }) })] })] }) })] }), _jsxs("div", { className: "cls_my_settings_layout_profile_information_section bg-white rounded-lg border border-[var(--hazo-border)] p-6", children: [_jsx("h2", { className: "cls_my_settings_layout_section_heading text-lg font-semibold text-[var(--hazo-text-primary)] mb-4", children: profileInformationLabel }), _jsxs("div", { className: "cls_my_settings_layout_profile_information_fields grid grid-cols-1 md:grid-cols-2 gap-6", children: [userFields.show_name_field && (_jsx(EditableField, { label: "Full Name", value: settings.name, type: "text", placeholder: "Enter your full name", onSave: settings.handleNameSave, validation: validateName, disabled: settings.loading, ariaLabel: "Full name input field" })), userFields.show_email_field && (_jsx(EditableField, { label: "Email Address", value: settings.email, type: "email", placeholder: "Enter your email address", onSave: settings.handleEmailSave, validation: validateEmail, disabled: settings.loading, ariaLabel: "Email address input field" }))] })] }), userFields.show_password_field && (_jsxs("div", { className: "cls_my_settings_layout_password_section bg-white rounded-lg border border-[var(--hazo-border)] p-6", children: [_jsx("h2", { className: "cls_my_settings_layout_section_heading text-lg font-semibold text-[var(--hazo-text-primary)] mb-4", children: passwordLabel }), _jsxs("div", { className: "cls_my_settings_layout_password_fields flex flex-col gap-6", children: [_jsx(FormFieldWrapper, { fieldId: "current-password", label: currentPasswordLabel, input: _jsx(PasswordField, { inputId: "current-password", ariaLabel: currentPasswordLabel, value: ((_a = settings.passwordFields) === null || _a === void 0 ? void 0 : _a.currentPassword) || "", placeholder: "Enter your current password", autoComplete: "current-password", isVisible: ((_b = settings.passwordFields) === null || _b === void 0 ? void 0 : _b.currentPasswordVisible) || false, onChange: (value) => settings.handlePasswordFieldChange("currentPassword", value), onToggleVisibility: () => settings.togglePasswordVisibility("currentPassword"), errorMessage: (_d = (_c = settings.passwordFields) === null || _c === void 0 ? void 0 : _c.errors) === null || _d === void 0 ? void 0 : _d.currentPassword }) }), _jsxs("div", { className: "cls_my_settings_layout_password_fields_row grid grid-cols-1 md:grid-cols-2 gap-6", children: [_jsx(FormFieldWrapper, { fieldId: "new-password", label: newPasswordLabel, input: _jsx(PasswordField, { inputId: "new-password", ariaLabel: newPasswordLabel, value: ((_e = settings.passwordFields) === null || _e === void 0 ? void 0 : _e.newPassword) || "", placeholder: "Enter your new password", autoComplete: "new-password", isVisible: ((_f = settings.passwordFields) === null || _f === void 0 ? void 0 : _f.newPasswordVisible) || false, onChange: (value) => settings.handlePasswordFieldChange("newPassword", value), onToggleVisibility: () => settings.togglePasswordVisibility("newPassword"), errorMessage: (_h = (_g = settings.passwordFields) === null || _g === void 0 ? void 0 : _g.errors) === null || _h === void 0 ? void 0 : _h.newPassword }) }), _jsx(FormFieldWrapper, { fieldId: "confirm-password", label: confirmPasswordLabel, input: _jsx(PasswordField, { inputId: "confirm-password", ariaLabel: confirmPasswordLabel, value: ((_j = settings.passwordFields) === null || _j === void 0 ? void 0 : _j.confirmPassword) || "", placeholder: "Confirm your new password", autoComplete: "new-password", isVisible: ((_k = settings.passwordFields) === null || _k === void 0 ? void 0 : _k.confirmPasswordVisible) || false, onChange: (value) => settings.handlePasswordFieldChange("confirmPassword", value), onToggleVisibility: () => settings.togglePasswordVisibility("confirmPassword"), errorMessage: (_m = (_l = settings.passwordFields) === null || _l === void 0 ? void 0 : _l.errors) === null || _m === void 0 ? void 0 : _m.confirmPassword }) })] })] }), _jsx("div", { className: "cls_my_settings_layout_password_actions flex justify-end mt-4", children: _jsx(Button, { type: "button", onClick: settings.handlePasswordSave, disabled: settings.loading || settings.isPasswordSaveDisabled, className: "cls_my_settings_layout_save_password_button", style: {
|
|
34
|
+
return (_jsx(UnauthorizedGuard, { message: unauthorizedMessage, loginButtonLabel: loginButtonLabel, loginPath: loginPath, children: _jsxs("div", { className: cn("cls_my_settings_layout flex flex-col gap-6 p-6 w-full", className), children: [_jsxs("div", { className: "cls_my_settings_layout_header flex flex-col gap-2", children: [_jsx("h1", { className: "cls_my_settings_layout_heading text-3xl font-bold text-[var(--hazo-text-primary)]", children: heading }), _jsx("p", { className: "cls_my_settings_layout_subheading text-[var(--hazo-text-muted)]", children: subHeading })] }), _jsxs("div", { className: "cls_my_settings_layout_profile_photo_section bg-white rounded-lg border border-[var(--hazo-border)] p-6", children: [_jsx("h2", { className: "cls_my_settings_layout_section_heading text-lg font-semibold text-[var(--hazo-text-primary)] mb-4", children: profilePhotoLabel }), _jsx("div", { className: "cls_my_settings_layout_profile_photo_content flex flex-col items-center", children: _jsxs("div", { className: "cls_my_settings_layout_profile_photo_display relative", children: [_jsx(ProfilePictureDisplay, { profilePictureUrl: settings.profilePictureUrl, name: settings.name, email: settings.email, onEdit: settings.handleProfilePictureEdit }), _jsxs("div", { className: "cls_my_settings_layout_profile_photo_actions absolute left-0 right-0 flex items-center justify-between px-2", style: { bottom: '-20px' }, children: [_jsx(Button, { type: "button", onClick: settings.handleProfilePictureEdit, disabled: settings.loading, variant: "ghost", size: "icon", className: "cls_my_settings_layout_upload_photo_button", "aria-label": uploadPhotoButtonLabel, children: _jsx(Pencil, { className: "h-4 w-4", "aria-hidden": "true" }) }), _jsx(Button, { type: "button", onClick: settings.handleProfilePictureRemove, disabled: settings.loading || !settings.profilePictureUrl, variant: "ghost", size: "icon", className: "cls_my_settings_layout_remove_photo_button text-red-600 hover:text-red-700 hover:bg-red-50", "aria-label": removePhotoButtonLabel, children: _jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" }) })] })] }) })] }), _jsxs("div", { className: "cls_my_settings_layout_profile_information_section bg-white rounded-lg border border-[var(--hazo-border)] p-6", children: [_jsx("h2", { className: "cls_my_settings_layout_section_heading text-lg font-semibold text-[var(--hazo-text-primary)] mb-4", children: profileInformationLabel }), _jsxs("div", { className: "cls_my_settings_layout_profile_information_fields grid grid-cols-1 md:grid-cols-2 gap-6", children: [userFields.show_name_field && (_jsx(EditableField, { label: "Full Name", value: settings.name, type: "text", placeholder: "Enter your full name", onSave: settings.handleNameSave, validation: validateName, disabled: settings.loading, ariaLabel: "Full name input field" })), userFields.show_email_field && (_jsx(EditableField, { label: "Email Address", value: settings.email, type: "email", placeholder: "Enter your email address", onSave: settings.handleEmailSave, validation: validateEmail, disabled: settings.loading, ariaLabel: "Email address input field" }))] })] }), userFields.show_password_field && settings.hasPassword && (_jsxs("div", { className: "cls_my_settings_layout_password_section bg-white rounded-lg border border-[var(--hazo-border)] p-6", children: [_jsx("h2", { className: "cls_my_settings_layout_section_heading text-lg font-semibold text-[var(--hazo-text-primary)] mb-4", children: passwordLabel }), _jsxs("div", { className: "cls_my_settings_layout_password_fields flex flex-col gap-6", children: [_jsx(FormFieldWrapper, { fieldId: "current-password", label: currentPasswordLabel, input: _jsx(PasswordField, { inputId: "current-password", ariaLabel: currentPasswordLabel, value: ((_a = settings.passwordFields) === null || _a === void 0 ? void 0 : _a.currentPassword) || "", placeholder: "Enter your current password", autoComplete: "current-password", isVisible: ((_b = settings.passwordFields) === null || _b === void 0 ? void 0 : _b.currentPasswordVisible) || false, onChange: (value) => settings.handlePasswordFieldChange("currentPassword", value), onToggleVisibility: () => settings.togglePasswordVisibility("currentPassword"), errorMessage: (_d = (_c = settings.passwordFields) === null || _c === void 0 ? void 0 : _c.errors) === null || _d === void 0 ? void 0 : _d.currentPassword }) }), _jsxs("div", { className: "cls_my_settings_layout_password_fields_row grid grid-cols-1 md:grid-cols-2 gap-6", children: [_jsx(FormFieldWrapper, { fieldId: "new-password", label: newPasswordLabel, input: _jsx(PasswordField, { inputId: "new-password", ariaLabel: newPasswordLabel, value: ((_e = settings.passwordFields) === null || _e === void 0 ? void 0 : _e.newPassword) || "", placeholder: "Enter your new password", autoComplete: "new-password", isVisible: ((_f = settings.passwordFields) === null || _f === void 0 ? void 0 : _f.newPasswordVisible) || false, onChange: (value) => settings.handlePasswordFieldChange("newPassword", value), onToggleVisibility: () => settings.togglePasswordVisibility("newPassword"), errorMessage: (_h = (_g = settings.passwordFields) === null || _g === void 0 ? void 0 : _g.errors) === null || _h === void 0 ? void 0 : _h.newPassword }) }), _jsx(FormFieldWrapper, { fieldId: "confirm-password", label: confirmPasswordLabel, input: _jsx(PasswordField, { inputId: "confirm-password", ariaLabel: confirmPasswordLabel, value: ((_j = settings.passwordFields) === null || _j === void 0 ? void 0 : _j.confirmPassword) || "", placeholder: "Confirm your new password", autoComplete: "new-password", isVisible: ((_k = settings.passwordFields) === null || _k === void 0 ? void 0 : _k.confirmPasswordVisible) || false, onChange: (value) => settings.handlePasswordFieldChange("confirmPassword", value), onToggleVisibility: () => settings.togglePasswordVisibility("confirmPassword"), errorMessage: (_m = (_l = settings.passwordFields) === null || _l === void 0 ? void 0 : _l.errors) === null || _m === void 0 ? void 0 : _m.confirmPassword }) })] })] }), _jsx("div", { className: "cls_my_settings_layout_password_actions flex justify-end mt-4", children: _jsx(Button, { type: "button", onClick: settings.handlePasswordSave, disabled: settings.loading || settings.isPasswordSaveDisabled, className: "cls_my_settings_layout_save_password_button", style: {
|
|
33
35
|
backgroundColor: resolvedButtonPalette.submitBackground,
|
|
34
36
|
color: resolvedButtonPalette.submitText,
|
|
35
|
-
}, "aria-label": "Save password", children: "Save Password" }) })] })), _jsx(ProfilePictureDialog, { open: settings.profilePictureDialogOpen, onOpenChange: (open) => {
|
|
37
|
+
}, "aria-label": "Save password", children: "Save Password" }) })] })), !settings.hasPassword && settings.googleConnected && (_jsx(SetPasswordSection, { passwordRequirements: password_requirements, onPasswordSet: settings.refreshUserData, parentLoading: settings.loading })), _jsx(ConnectedAccountsSection, { googleConnected: settings.googleConnected || false, email: settings.email, loading: settings.loading }), _jsx(ProfilePictureDialog, { open: settings.profilePictureDialogOpen, onOpenChange: (open) => {
|
|
36
38
|
if (open) {
|
|
37
39
|
settings.handleProfilePictureEdit();
|
|
38
40
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type GoogleIconProps = {
|
|
2
|
+
className?: string;
|
|
3
|
+
width?: number;
|
|
4
|
+
height?: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Google logo SVG component
|
|
8
|
+
* Uses official Google brand colors
|
|
9
|
+
*/
|
|
10
|
+
export declare function GoogleIcon({ className, width, height, }: GoogleIconProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export default GoogleIcon;
|
|
12
|
+
//# sourceMappingURL=google_icon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google_icon.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/google_icon.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EACzB,SAAc,EACd,KAAU,EACV,MAAW,GACZ,EAAE,eAAe,2CA6BjB;AAED,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Google logo SVG component
|
|
4
|
+
* Uses official Google brand colors
|
|
5
|
+
*/
|
|
6
|
+
export function GoogleIcon({ className = "", width = 20, height = 20, }) {
|
|
7
|
+
return (_jsxs("svg", { className: className, width: width, height: height, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [_jsx("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z", fill: "#4285F4" }), _jsx("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: "#34A853" }), _jsx("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z", fill: "#FBBC05" }), _jsx("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: "#EA4335" })] }));
|
|
8
|
+
}
|
|
9
|
+
export default GoogleIcon;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type GoogleSignInButtonProps = {
|
|
2
|
+
/** Text displayed on the button */
|
|
3
|
+
label?: string;
|
|
4
|
+
/** Custom click handler - if not provided, redirects to Google OAuth */
|
|
5
|
+
onClick?: () => void;
|
|
6
|
+
/** Disable the button */
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
/** Additional CSS classes */
|
|
9
|
+
className?: string;
|
|
10
|
+
/** Callback URL after OAuth (default: /api/hazo_auth/oauth/google/callback) */
|
|
11
|
+
callbackUrl?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Google Sign-In button component
|
|
15
|
+
* Displays the Google logo with configurable text
|
|
16
|
+
* Initiates the Google OAuth flow when clicked
|
|
17
|
+
* Uses next-auth/react signIn function for proper OAuth flow
|
|
18
|
+
*/
|
|
19
|
+
export declare function GoogleSignInButton({ label, onClick, disabled, className, callbackUrl, }: GoogleSignInButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export default GoogleSignInButton;
|
|
21
|
+
//# sourceMappingURL=google_sign_in_button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google_sign_in_button.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/google_sign_in_button.tsx"],"names":[],"mappings":"AAaA,MAAM,MAAM,uBAAuB,GAAG;IACpC,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,yBAAyB;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAGF;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,KAA8B,EAC9B,OAAO,EACP,QAAgB,EAChB,SAAS,EACT,WAAoD,GACrD,EAAE,uBAAuB,2CAmDzB;AAED,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// file_description: Google Sign-In button component with official Google icon
|
|
2
|
+
// section: client_directive
|
|
3
|
+
"use client";
|
|
4
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
|
+
// section: imports
|
|
6
|
+
import { Button } from "../../../ui/button";
|
|
7
|
+
import { GoogleIcon } from "./google_icon";
|
|
8
|
+
import { cn } from "../../../../lib/utils";
|
|
9
|
+
import { useState } from "react";
|
|
10
|
+
import { Loader2 } from "lucide-react";
|
|
11
|
+
import { signIn } from "next-auth/react";
|
|
12
|
+
// section: component
|
|
13
|
+
/**
|
|
14
|
+
* Google Sign-In button component
|
|
15
|
+
* Displays the Google logo with configurable text
|
|
16
|
+
* Initiates the Google OAuth flow when clicked
|
|
17
|
+
* Uses next-auth/react signIn function for proper OAuth flow
|
|
18
|
+
*/
|
|
19
|
+
export function GoogleSignInButton({ label = "Continue with Google", onClick, disabled = false, className, callbackUrl = "/api/hazo_auth/oauth/google/callback", }) {
|
|
20
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
21
|
+
const handleClick = async () => {
|
|
22
|
+
if (disabled || isLoading)
|
|
23
|
+
return;
|
|
24
|
+
if (onClick) {
|
|
25
|
+
onClick();
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
setIsLoading(true);
|
|
29
|
+
try {
|
|
30
|
+
// Use next-auth/react signIn function for proper OAuth flow
|
|
31
|
+
// redirect: true (default) lets NextAuth handle the full flow
|
|
32
|
+
// including the redirect callback which goes to our custom callback URL
|
|
33
|
+
console.log("[GoogleSignInButton] Starting Google OAuth with callbackUrl:", callbackUrl);
|
|
34
|
+
await signIn("google", {
|
|
35
|
+
callbackUrl,
|
|
36
|
+
redirect: true,
|
|
37
|
+
});
|
|
38
|
+
// Note: redirect: true means this code won't execute after success
|
|
39
|
+
// as the browser will be redirected
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error("[GoogleSignInButton] Sign-in exception:", error);
|
|
43
|
+
alert(`Google Sign-In Exception: ${error}`);
|
|
44
|
+
setIsLoading(false);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
return (_jsxs(Button, { type: "button", variant: "outline", onClick: handleClick, disabled: disabled || isLoading, className: cn("cls_google_sign_in_button w-full flex items-center justify-center gap-3 h-11 border-slate-300 hover:bg-slate-50 hover:border-slate-400 transition-colors", className), "aria-label": label, children: [isLoading ? (_jsx(Loader2, { className: "h-5 w-5 animate-spin text-slate-600", "aria-hidden": "true" })) : (_jsx(GoogleIcon, { className: "h-5 w-5" })), _jsx("span", { className: "text-slate-700 font-medium", children: isLoading ? "Signing in..." : label })] }));
|
|
49
|
+
}
|
|
50
|
+
export default GoogleSignInButton;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type OAuthDividerProps = {
|
|
2
|
+
/** Text displayed in the divider */
|
|
3
|
+
text?: string;
|
|
4
|
+
/** Additional CSS classes */
|
|
5
|
+
className?: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Visual divider component to separate OAuth buttons from email/password form
|
|
9
|
+
* Displays a horizontal line with text in the center
|
|
10
|
+
*/
|
|
11
|
+
export declare function OAuthDivider({ text, className, }: OAuthDividerProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export default OAuthDivider;
|
|
13
|
+
//# sourceMappingURL=oauth_divider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth_divider.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/components/oauth_divider.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,oCAAoC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAGF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAC3B,IAA+B,EAC/B,SAAS,GACV,EAAE,iBAAiB,2CAoBnB;AAED,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// file_description: Visual divider between OAuth buttons and email/password form
|
|
3
|
+
// section: imports
|
|
4
|
+
import { cn } from "../../../../lib/utils";
|
|
5
|
+
// section: component
|
|
6
|
+
/**
|
|
7
|
+
* Visual divider component to separate OAuth buttons from email/password form
|
|
8
|
+
* Displays a horizontal line with text in the center
|
|
9
|
+
*/
|
|
10
|
+
export function OAuthDivider({ text = "or continue with email", className, }) {
|
|
11
|
+
return (_jsxs("div", { className: cn("cls_oauth_divider relative my-6", className), role: "separator", "aria-orientation": "horizontal", children: [_jsx("div", { className: "absolute inset-0 flex items-center", "aria-hidden": "true", children: _jsx("span", { className: "w-full border-t border-slate-200" }) }), _jsx("div", { className: "relative flex justify-center text-sm", children: _jsx("span", { className: "bg-white px-3 text-slate-500", children: text }) })] }));
|
|
12
|
+
}
|
|
13
|
+
export default OAuthDivider;
|
|
@@ -13,6 +13,9 @@ export type AuthStatusData = {
|
|
|
13
13
|
permissions?: string[];
|
|
14
14
|
permission_ok?: boolean;
|
|
15
15
|
missing_permissions?: string[];
|
|
16
|
+
auth_providers?: string;
|
|
17
|
+
has_password?: boolean;
|
|
18
|
+
google_connected?: boolean;
|
|
16
19
|
loading: boolean;
|
|
17
20
|
};
|
|
18
21
|
export type AuthStatus = AuthStatusData & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use_auth_status.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/hooks/use_auth_status.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC9D,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"use_auth_status.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/shared/hooks/use_auth_status.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC9D,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG;IACxC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAMF;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAIlD;AAGD,wBAAgB,eAAe,IAAI,UAAU,CA2E5C"}
|