@sonic-equipment/ui 156.0.0 → 158.0.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.
Files changed (85) hide show
  1. package/dist/algolia/algolia-sort-by.js +1 -1
  2. package/dist/buttons/add-to-cart-button/add-to-cart-button.js +8 -11
  3. package/dist/buttons/add-to-cart-button/add-to-cart-button.module.css.js +1 -1
  4. package/dist/cards/orderline-card/orderline-card.js +1 -2
  5. package/dist/cards/product-card/connected-product-card.d.ts +2 -2
  6. package/dist/cards/product-card/product-card.js +1 -2
  7. package/dist/cards/product-card/product-card.module.css.js +1 -1
  8. package/dist/cart-totals/cart-totals.js +1 -1
  9. package/dist/country-select/country-select.d.ts +1 -0
  10. package/dist/country-select/country-select.js +3 -3
  11. package/dist/exports.d.ts +13 -1
  12. package/dist/filters/pagination/pagination.js +3 -1
  13. package/dist/forms/checkbox/checkbox.d.ts +13 -4
  14. package/dist/forms/checkbox/checkbox.js +6 -2
  15. package/dist/forms/checkbox/checkbox.module.css.js +1 -1
  16. package/dist/forms/checkbox-field/checkbox-field.d.ts +10 -0
  17. package/dist/forms/checkbox-field/checkbox-field.js +16 -0
  18. package/dist/forms/checkbox-field/checkbox-field.module.css.js +3 -0
  19. package/dist/forms/color-checkbox/color-checkbox.d.ts +5 -3
  20. package/dist/forms/color-checkbox/color-checkbox.js +7 -3
  21. package/dist/forms/form/form-field-layout.d.ts +10 -0
  22. package/dist/forms/form/form-field-layout.js +10 -0
  23. package/dist/forms/form/form-field-layout.module.css.js +3 -0
  24. package/dist/forms/form/form-segment-group.d.ts +6 -0
  25. package/dist/forms/form/form-segment-group.js +13 -0
  26. package/dist/forms/form/form-segment-group.module.css.js +3 -0
  27. package/dist/forms/form/form-segment.d.ts +6 -0
  28. package/dist/forms/form/form-segment.js +9 -0
  29. package/dist/forms/form/form-segment.module.css.js +3 -0
  30. package/dist/forms/form/form.d.ts +16 -0
  31. package/dist/forms/form/form.js +27 -0
  32. package/dist/forms/form/form.module.css.js +3 -0
  33. package/dist/forms/input/input.d.ts +2 -0
  34. package/dist/forms/input/input.js +2 -2
  35. package/dist/forms/label/label.js +1 -1
  36. package/dist/forms/number-field/number-field.d.ts +9 -9
  37. package/dist/forms/number-field/number-field.js +18 -14
  38. package/dist/forms/number-field/number-field.module.css.js +1 -1
  39. package/dist/forms/select/select.js +3 -3
  40. package/dist/forms/select-field/select-field.d.ts +27 -0
  41. package/dist/forms/select-field/select-field.js +31 -0
  42. package/dist/forms/select-field/select-field.module.css.js +3 -0
  43. package/dist/forms/switch/switch.d.ts +12 -4
  44. package/dist/forms/switch/switch.js +6 -2
  45. package/dist/forms/switch/switch.module.css.js +1 -1
  46. package/dist/forms/switch-field/switch-field.d.ts +10 -0
  47. package/dist/forms/switch-field/switch-field.js +18 -0
  48. package/dist/forms/switch-field/switch-field.module.css.js +3 -0
  49. package/dist/forms/text-field/password-reveal-toggle/password-reveal-toggle.js +1 -1
  50. package/dist/forms/text-field/password-reveal-toggle/password-reveal-toggle.module.css.js +1 -1
  51. package/dist/forms/text-field/text-field.d.ts +32 -20
  52. package/dist/forms/text-field/text-field.js +13 -10
  53. package/dist/forms/text-field/text-field.module.css.js +1 -1
  54. package/dist/forms/textarea/textarea.d.ts +1 -0
  55. package/dist/index.js +14 -2
  56. package/dist/intl/translation-id.d.ts +1 -1
  57. package/dist/pages/account/components/create-account-form/create-account-form.d.ts +11 -0
  58. package/dist/pages/account/components/create-account-form/create-account-form.js +89 -0
  59. package/dist/pages/account/components/create-account-form/create-account-form.module.css.js +3 -0
  60. package/dist/{sign-in-form → pages/account/components/sign-in-form}/sign-in-form.d.ts +3 -3
  61. package/dist/pages/account/components/sign-in-form/sign-in-form.js +71 -0
  62. package/dist/pages/account/components/sign-in-form/sign-in-form.module.css.js +3 -0
  63. package/dist/pages/account/create-account-page/create-account-page.d.ts +3 -0
  64. package/dist/pages/account/create-account-page/create-account-page.js +45 -0
  65. package/dist/pages/account/layouts/sign-in-page-layout/sign-in-page-background-image.d.ts +6 -0
  66. package/dist/pages/account/layouts/sign-in-page-layout/sign-in-page-background-image.js +9 -0
  67. package/dist/pages/account/layouts/sign-in-page-layout/sign-in-page-layout.d.ts +1 -1
  68. package/dist/pages/account/layouts/sign-in-page-layout/sign-in-page-layout.js +2 -1
  69. package/dist/pages/account/sign-in-page/sign-in-page.js +3 -10
  70. package/dist/pages/checkout/order-confirmation-page/order-confirmation-page-content.js +1 -1
  71. package/dist/pages/checkout/payment-page/components/payment.js +6 -5
  72. package/dist/pages/checkout/shipping-page/shipping-page-content.js +1 -1
  73. package/dist/pages/checkout/shipping-page/shipping-page.js +1 -1
  74. package/dist/shared/api/storefront/hooks/authentication/use-create-account.d.ts +3 -0
  75. package/dist/shared/api/storefront/hooks/authentication/use-create-account.js +28 -0
  76. package/dist/shared/api/storefront/services/authentication-service.d.ts +23 -0
  77. package/dist/shared/api/storefront/services/authentication-service.js +40 -2
  78. package/dist/shared/hooks/use-script.d.ts +3 -3
  79. package/dist/shared/model/account.d.ts +19 -0
  80. package/dist/shared/model/account.js +4 -0
  81. package/dist/styles.css +844 -613
  82. package/dist/tooltip/tooltip.js +1 -1
  83. package/package.json +1 -1
  84. package/dist/sign-in-form/sign-in-form.js +0 -59
  85. package/dist/sign-in-form/sign-in-form.module.css.js +0 -3
@@ -0,0 +1,89 @@
1
+ "use client";
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { useState } from 'react';
4
+ import { Button } from '../../../../buttons/button/button.js';
5
+ import { CountrySelect } from '../../../../country-select/country-select.js';
6
+ import { useCountries } from '../../../../country-select/hooks/use-countries.js';
7
+ import { Form } from '../../../../forms/form/form.js';
8
+ import { FormSegment } from '../../../../forms/form/form-segment.js';
9
+ import { FormSegmentGroup } from '../../../../forms/form/form-segment-group.js';
10
+ import { SwitchField } from '../../../../forms/switch-field/switch-field.js';
11
+ import { TextField } from '../../../../forms/text-field/text-field.js';
12
+ import { isCountryCode } from '../../../../intl/types.js';
13
+ import { useFormattedMessage } from '../../../../intl/use-formatted-message.js';
14
+ import { validatePassword } from '../../../../shared/model/account.js';
15
+ import { validateEmail } from '../../../../shared/model/address.js';
16
+ import { Heading } from '../../../../typography/heading/heading.js';
17
+ import styles from './create-account-form.module.css.js';
18
+
19
+ function CreateAccountForm({ errorType, isDisabled: _isDisabled = false, isPendingCreateAccount, onSubmit, }) {
20
+ const t = useFormattedMessage();
21
+ const title = t('create account');
22
+ const isDisabled = isPendingCreateAccount || _isDisabled;
23
+ const [isPrivateAccount, setIsPrivateAccount] = useState(false);
24
+ const [password, setPassword] = useState('');
25
+ const [passwordConfirm, setPasswordConfirm] = useState('');
26
+ const [lastName, setLastName] = useState('');
27
+ const [companyName, setCompanyName] = useState('');
28
+ const errorMessages = {
29
+ 'Unexpected error': t('An unexpected error occured. Please try again.'),
30
+ };
31
+ const { countries, currentCountry: defaultSelectedCountry, isFetching: isLoadingCountries, } = useCountries({ enabled: true });
32
+ // form submit handler
33
+ const handleSubmit = ({ formData }) => {
34
+ const companyName = formData.get('companyName')?.toString() || '';
35
+ const firstName = formData.get('firstName')?.toString();
36
+ const lastName = formData.get('lastName')?.toString() || '';
37
+ const email = formData.get('email')?.toString();
38
+ const password = formData.get('password')?.toString();
39
+ const isSubscribed = Boolean(formData.get('isSubscribed'));
40
+ const isPrivateAccount = Boolean(formData.get('isPrivateAccount'));
41
+ const countryId = formData.get('countrySelect')?.toString();
42
+ const countryCode = countries?.find(country => country.id === countryId)?.abbreviation;
43
+ if (!isCountryCode(countryCode))
44
+ throw new Error('CountryCode value is required');
45
+ if (!email)
46
+ throw new Error('Email value is required');
47
+ if (!password)
48
+ throw new Error('Password value is required');
49
+ if (!isPrivateAccount && !companyName)
50
+ throw new Error('CompanyName value is required');
51
+ if (isPrivateAccount && !lastName)
52
+ throw new Error('LastName value is required');
53
+ onSubmit({
54
+ data: {
55
+ companyName,
56
+ countryCode,
57
+ email,
58
+ firstName,
59
+ isPrivateAccount,
60
+ isSubscribed,
61
+ lastName,
62
+ password,
63
+ },
64
+ });
65
+ };
66
+ const isMismatchingPasswords = () => {
67
+ if (!passwordConfirm)
68
+ return;
69
+ if (passwordConfirm === password)
70
+ return false;
71
+ return true;
72
+ };
73
+ // form header
74
+ const header = (jsx(Heading, { "data-test-selector": "PageTitle", italic: true, size: "m", tag: "h1", uppercase: true, children: title }));
75
+ // form footer
76
+ const footer = (jsx(FormSegment, { children: jsx(Button, { "data-test-selector": "createAccount_createButton", isDisabled: isDisabled, isLoading: isPendingCreateAccount && 'Creating account...', type: "submit", withArrow: true, children: t('create account') }) }));
77
+ return (jsxs(Form, { "aria-label": title, autoComplete: true, className: styles['create-account-form'], errorMessage: errorType && errorMessages[errorType], footer: footer, header: header, onSubmit: handleSubmit, title: title, children: [jsxs(FormSegmentGroup, { children: [jsx(FormSegment, { children: jsx(TextField, { autoComplete: "username", "data-test-selector": "createAccount_email", inputMode: "email", isDisabled: isDisabled, isRequired: true, label: t('Email'), name: "email", type: "email", validate: value => {
78
+ if (!value)
79
+ return value;
80
+ return (validateEmail(value) || t('Please enter a valid email address'));
81
+ } }) }), jsx(FormSegment, { children: jsx(TextField, { autoComplete: "new-password", "data-test-selector": "createAccount_password", info: "Password must be at least 8 characters long, include at least one number, at least one lowercase character, at least one uppercase character and at least one non-alphanumeric character.", isDisabled: isDisabled, isRequired: true, label: t('Password'), name: "password", onChange: setPassword, type: "password", validate: value => {
82
+ if (!value)
83
+ return;
84
+ return (validatePassword(value) ||
85
+ t('Password does not meet requirements'));
86
+ } }) }), jsx(FormSegment, { children: jsx(TextField, { autoComplete: "new-password", customErrorMessage: "Passwords do not match", "data-test-selector": "createAccount_passwordConfirm", isDisabled: isDisabled, isInvalid: isMismatchingPasswords(), isRequired: true, label: t('Confirm password'), onChange: setPasswordConfirm, type: "password", value: passwordConfirm }) })] }), jsxs(FormSegmentGroup, { children: [jsx(FormSegment, { children: jsx(SwitchField, { isDisabled: isDisabled, isSelected: isPrivateAccount, name: "isPrivateAccount", onChange: setIsPrivateAccount, children: t('Private account') }) }), jsx(FormSegment, { children: jsx(TextField, { autoComplete: "organization", "data-test-selector": "createAccount_companyName", isDisabled: isDisabled, isRequired: !isPrivateAccount, label: t('Company name'), name: "companyName", onChange: setCompanyName, value: companyName }, `companyName-${Boolean(isPrivateAccount)}`) }), jsx(FormSegment, { children: jsx(TextField, { autoComplete: "given-name", "data-test-selector": "createAccount_firstName", isDisabled: isDisabled, label: t('First name'), name: "firstName" }) }), jsx(FormSegment, { children: jsx(TextField, { autoComplete: "family-name", "data-test-selector": "createAccount_lastName", isDisabled: isDisabled, isRequired: isPrivateAccount, label: t('Last name'), name: "lastName", onChange: setLastName, value: lastName }, `lastname-${Boolean(isPrivateAccount)}`) }), jsx(FormSegment, { children: jsx(CountrySelect, { isRequired: true, countries: countries || [], "data-test-selector": "createAccount_countrySelect", defaultSelectedCountry: defaultSelectedCountry, isDisabled: isLoadingCountries || isDisabled, isLoading: isLoadingCountries, name: "countrySelect" }, defaultSelectedCountry?.id) })] }), jsx(FormSegmentGroup, { children: jsx(FormSegment, { children: jsx(SwitchField, { isDisabled: isDisabled, name: "isSubscribed", children: t('Sign me up for newsletters and product updates') }) }) })] }));
87
+ }
88
+
89
+ export { CreateAccountForm };
@@ -0,0 +1,3 @@
1
+ var styles = {"create-account-form":"create-account-form-module-TMU8F"};
2
+
3
+ export { styles as default };
@@ -8,11 +8,11 @@ interface GuestLoginData {
8
8
  guestSignIn: true;
9
9
  }
10
10
  export type SubmitData = LoginData | GuestLoginData;
11
- export type ErrorMessage = 'Access denied' | 'Unexpected error';
11
+ export type SignInErrorTypes = 'Access denied' | 'Unexpected error' | undefined;
12
12
  export interface SignInFormProps {
13
13
  allowGuestSignIn?: boolean;
14
14
  createAccountPath: string;
15
- errorMessage?: ErrorMessage;
15
+ errorType?: SignInErrorTypes;
16
16
  initialEmail?: string;
17
17
  initialRememberMe?: boolean;
18
18
  isDisabled?: boolean;
@@ -23,5 +23,5 @@ export interface SignInFormProps {
23
23
  data: SubmitData;
24
24
  }) => void;
25
25
  }
26
- export declare function SignInForm({ allowGuestSignIn, createAccountPath, errorMessage, initialEmail, initialRememberMe, isDisabled: _isDisabled, isPendingGuestSignIn, isPendingUserSignIn, onRecoverPasswordDialogOpen, onSubmit, }: SignInFormProps): import("react/jsx-runtime").JSX.Element;
26
+ export declare function SignInForm({ allowGuestSignIn, createAccountPath, errorType, initialEmail, initialRememberMe, isDisabled: _isDisabled, isPendingGuestSignIn, isPendingUserSignIn, onRecoverPasswordDialogOpen, onSubmit, }: SignInFormProps): import("react/jsx-runtime").JSX.Element;
27
27
  export {};
@@ -0,0 +1,71 @@
1
+ "use client";
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { Button } from '../../../../buttons/button/button.js';
4
+ import { Link } from '../../../../buttons/link/link.js';
5
+ import { Form } from '../../../../forms/form/form.js';
6
+ import { FormSegment } from '../../../../forms/form/form-segment.js';
7
+ import { FormSegmentGroup } from '../../../../forms/form/form-segment-group.js';
8
+ import { SwitchField } from '../../../../forms/switch-field/switch-field.js';
9
+ import { TextField } from '../../../../forms/text-field/text-field.js';
10
+ import { useFormattedMessage } from '../../../../intl/use-formatted-message.js';
11
+ import { validateEmail } from '../../../../shared/model/address.js';
12
+ import { voidFunction } from '../../../../shared/model/defaults.js';
13
+ import { RouteLink } from '../../../../shared/routing/route-link.js';
14
+ import { Heading } from '../../../../typography/heading/heading.js';
15
+ import styles from './sign-in-form.module.css.js';
16
+
17
+ const GUEST_SIGN_IN_BUTTON_NAME = 'guestSignin';
18
+ function SignInForm({ allowGuestSignIn = false, createAccountPath, errorType, initialEmail, initialRememberMe, isDisabled: _isDisabled = false, isPendingGuestSignIn = false, isPendingUserSignIn = false, onRecoverPasswordDialogOpen = voidFunction, onSubmit = voidFunction, }) {
19
+ const t = useFormattedMessage();
20
+ const title = t('sign in');
21
+ const isDisabled = isPendingUserSignIn || isPendingGuestSignIn || _isDisabled;
22
+ // form submit handler
23
+ const handleSubmit = ({ formData, submitter }) => {
24
+ // in case of guest sign-in submit
25
+ if (submitter?.name === GUEST_SIGN_IN_BUTTON_NAME) {
26
+ return onSubmit({
27
+ data: {
28
+ guestSignIn: true,
29
+ },
30
+ });
31
+ }
32
+ // regular sign-in
33
+ const email = formData.get('email')?.toString();
34
+ const password = formData.get('password')?.toString();
35
+ const rememberMe = Boolean(formData.get('rememberMe'));
36
+ if (!email)
37
+ throw new Error('Email value is required');
38
+ if (!password)
39
+ throw new Error('Password value is required');
40
+ onSubmit({
41
+ data: {
42
+ email,
43
+ guestSignIn: false,
44
+ password,
45
+ rememberMe,
46
+ },
47
+ });
48
+ };
49
+ // error message to display
50
+ const errorMessage = (function (type) {
51
+ switch (type) {
52
+ case undefined:
53
+ return;
54
+ case 'Access denied':
55
+ return t('Your email and password were not recognized.');
56
+ default:
57
+ return t('An unexpected error occured. Please try again.');
58
+ }
59
+ })(errorType);
60
+ // form header
61
+ const header = (jsx(Heading, { "data-test-selector": "PageTitle", italic: true, size: "m", tag: "h1", uppercase: true, children: title }));
62
+ // form footer
63
+ const footer = (jsxs(Fragment, { children: [jsx(FormSegment, { isFloating: true, children: jsx(SwitchField, { defaultSelected: initialRememberMe, isDisabled: isDisabled, name: "rememberMe", value: "true", children: t('Remember me') }) }), jsx(FormSegment, { children: jsx(Button, { "data-test-selector": "signIn_submit", isDisabled: isDisabled, isLoading: isPendingUserSignIn && t('Signing in…'), type: "submit", withArrow: true, children: t('sign in') }) }), jsxs(FormSegment, { children: [jsx("p", { className: styles['footer-options'], children: jsx(Link, { color: "primary", "data-test-selector": "signIn_forgotPassword", hasUnderline: true, isDisabled: isDisabled, onClick: onRecoverPasswordDialogOpen, children: t('Forgot password?') }) }), jsx("p", { className: styles['footer-options'], children: jsxs(Fragment, { children: [t('New user?'), ' ', jsx(RouteLink, { "data-test-selector": "signInCreateNewAccount_createNewAccount", hasUnderline: true, href: createAccountPath, isDisabled: isDisabled, children: t('create account') })] }) })] }), allowGuestSignIn && (jsx(FormSegment, { children: jsx(Button, { color: "secondary", isDisabled: isDisabled, isLoading: isPendingGuestSignIn && t('Signing in…'), isValidating: false, name: GUEST_SIGN_IN_BUTTON_NAME, type: "submit", value: "true", variant: "outline", children: t('Or continue as guest') }) }))] }));
64
+ return (jsx(Form, { autoComplete: true, className: styles['sign-in-form'], errorMessage: errorMessage, footer: footer, header: header, onSubmit: handleSubmit, title: title, children: jsxs(FormSegmentGroup, { children: [jsx(FormSegment, { children: jsx(TextField, { autoComplete: "username", "data-test-selector": "signIn_userName", defaultValue: initialEmail, inputMode: "email", isDisabled: isDisabled, isRequired: true, label: t('Email'), name: "email", showLabel: true, type: "email", validate: value => {
65
+ if (!value)
66
+ return value;
67
+ return (validateEmail(value) || t('Please enter a valid email address'));
68
+ } }) }), jsx(FormSegment, { children: jsx(TextField, { autoComplete: "current-password", "data-test-selector": "signIn_password", isDisabled: isDisabled, isRequired: true, label: t('Password'), name: "password", showLabel: true, type: "password" }) })] }) }));
69
+ }
70
+
71
+ export { SignInForm };
@@ -0,0 +1,3 @@
1
+ var styles = {"sign-in-form":"sign-in-form-module-BoJwR","footer-options":"sign-in-form-module-PQ4Ik"};
2
+
3
+ export { styles as default };
@@ -0,0 +1,3 @@
1
+ export declare function CreateAccountPage({ returnUrl }?: {
2
+ returnUrl?: string;
3
+ }): import("react/jsx-runtime").JSX.Element | undefined;
@@ -0,0 +1,45 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useMemo } from 'react';
3
+ import { FormattedMessage } from '../../../intl/formatted-message.js';
4
+ import { Dialog } from '../../../modals/dialog/dialog.js';
5
+ import { useCreateAccount } from '../../../shared/api/storefront/hooks/authentication/use-create-account.js';
6
+ import { useFetchSession } from '../../../shared/api/storefront/hooks/authentication/use-fetch-session.js';
7
+ import { ExistingAccountError } from '../../../shared/api/storefront/services/authentication-service.js';
8
+ import { RouteButton } from '../../../shared/routing/route-button.js';
9
+ import { useNavigate } from '../../../shared/routing/use-navigate.js';
10
+ import { Page } from '../../components/page/page.js';
11
+ import { LoadingPage } from '../../loading-page/loading-page.js';
12
+ import { PATHS } from '../../paths.js';
13
+ import { CreateAccountForm } from '../components/create-account-form/create-account-form.js';
14
+ import { SignInPageLayout } from '../layouts/sign-in-page-layout/sign-in-page-layout.js';
15
+
16
+ function CreateAccountPage({ returnUrl } = {}) {
17
+ const { navigate } = useNavigate();
18
+ const { data: session, isLoading: isLoadingSession } = useFetchSession();
19
+ const { error: errorCreateAccount, isPending: isPendingCreateAccount, isSuccess, mutate: createAccount, } = useCreateAccount();
20
+ const isExistingAccount = errorCreateAccount instanceof ExistingAccountError;
21
+ const isDisabled = isSuccess || isExistingAccount;
22
+ const continuePath = returnUrl && returnUrl !== PATHS.ACCOUNT_CREATE ? returnUrl : PATHS.ACCOUNT;
23
+ const isReturnToShipping = returnUrl === PATHS.CHECKOUT_SHIPPING;
24
+ const onSubmit = ({ data }) => {
25
+ createAccount(data, {
26
+ onSuccess() {
27
+ if (isReturnToShipping)
28
+ navigate(continuePath);
29
+ },
30
+ });
31
+ };
32
+ const errorType = useMemo(() => !errorCreateAccount || errorCreateAccount instanceof ExistingAccountError
33
+ ? undefined
34
+ : 'Unexpected error', [errorCreateAccount]);
35
+ if (isLoadingSession)
36
+ return jsx(LoadingPage, {});
37
+ if (session && session.isAuthenticated && !isSuccess && !isExistingAccount) {
38
+ // A user is already signed in (either as guest or not), redirect them
39
+ navigate(continuePath);
40
+ return;
41
+ }
42
+ return (jsxs(Page, { fluid: true, fullHeight: true, "data-test-selector": "createAccountPage", children: [jsx(SignInPageLayout, { fullHeight: true, children: jsx(CreateAccountForm, { errorType: errorType, isDisabled: isDisabled, isPendingCreateAccount: isPendingCreateAccount, onSubmit: onSubmit }) }), jsx(Dialog, { footer: jsx(RouteButton, { color: "primary", href: continuePath, size: "md", withArrow: true, children: jsx(FormattedMessage, { id: "Continue" }) }), hasCloseButton: false, isDismissable: false, isKeyboardDismissDisabled: true, isOpen: isSuccess, title: "Account created", children: jsx("p", { children: jsx(FormattedMessage, { id: "Your new Sonic Equipment account was succesfully created. You should receive an email soon with further instructions on how to activate this account. If you do not receive this email, please contact Customer Support." }) }) }), jsx(Dialog, { footer: jsx(RouteButton, { color: "primary", href: `${PATHS.SIGN_IN}${returnUrl ? `?returnUrl=${returnUrl}` : ''}`, size: "md", withArrow: true, children: jsx(FormattedMessage, { id: "Continue to sign in" }) }), hasCloseButton: false, isDismissable: false, isKeyboardDismissDisabled: true, isOpen: isExistingAccount, title: "Existing account", children: jsx("p", { children: jsx(FormattedMessage, { id: "The email address you entered is already associated with an existing account. Please sign in to this account or contact Customer Support." }) }) })] }));
43
+ }
44
+
45
+ export { CreateAccountPage };
@@ -0,0 +1,6 @@
1
+ export declare const SIGN_IN_PAGE_BACKGROUND_IMAGE: {
2
+ 1: string;
3
+ 2: string;
4
+ 3: string;
5
+ altText: string;
6
+ };
@@ -0,0 +1,9 @@
1
+ // TODO: We need larger images
2
+ const SIGN_IN_PAGE_BACKGROUND_IMAGE = {
3
+ 1: 'https://res.cloudinary.com/dkz9eknwh/image/upload/w_1658,h_1008/v1740665245/images/sign-in-page.webp',
4
+ 2: 'https://res.cloudinary.com/dkz9eknwh/image/upload/w_1658,h_1008/v1740665245/images/sign-in-page.webp',
5
+ 3: 'https://res.cloudinary.com/dkz9eknwh/image/upload/w_1658,h_1008/v1740665245/images/sign-in-page.webp',
6
+ altText: '',
7
+ };
8
+
9
+ export { SIGN_IN_PAGE_BACKGROUND_IMAGE };
@@ -3,6 +3,6 @@ import { ImageType } from '../../../../shared/model/image';
3
3
  export interface SignInPageLayoutProps {
4
4
  children?: ReactNode;
5
5
  fullHeight?: boolean;
6
- image: ImageType;
6
+ image?: ImageType;
7
7
  }
8
8
  export declare function SignInPageLayout({ children, fullHeight, image, }: SignInPageLayoutProps): import("react/jsx-runtime").JSX.Element;
@@ -2,9 +2,10 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import clsx from 'clsx';
3
3
  import { useIsBreakpoint } from '../../../../shared/hooks/use-is-breakpoint.js';
4
4
  import { Image } from '../../../../media/image/image.js';
5
+ import { SIGN_IN_PAGE_BACKGROUND_IMAGE } from './sign-in-page-background-image.js';
5
6
  import styles from './sign-in-page-layout.module.css.js';
6
7
 
7
- function SignInPageLayout({ children, fullHeight, image, }) {
8
+ function SignInPageLayout({ children, fullHeight, image = SIGN_IN_PAGE_BACKGROUND_IMAGE, }) {
8
9
  const isLg = useIsBreakpoint('lg');
9
10
  return (jsxs("div", { className: clsx(styles['sign-in-page-layout'], fullHeight && styles['full-height']), children: [jsx("div", { className: styles.main, children: children }), isLg && (jsx("div", { className: styles.side, children: jsx("div", { className: styles.image, children: jsx(Image, { image: image, title: "" }) }) }))] }));
10
11
  }
@@ -7,18 +7,11 @@ import { useSignIn } from '../../../shared/api/storefront/hooks/authentication/u
7
7
  import { isRequestError } from '../../../shared/fetch/request.js';
8
8
  import { useDisclosure } from '../../../shared/hooks/use-disclosure.js';
9
9
  import { useNavigate } from '../../../shared/routing/use-navigate.js';
10
- import { SignInForm } from '../../../sign-in-form/sign-in-form.js';
11
10
  import { Page } from '../../components/page/page.js';
12
11
  import { PATHS } from '../../paths.js';
12
+ import { SignInForm } from '../components/sign-in-form/sign-in-form.js';
13
13
  import { SignInPageLayout } from '../layouts/sign-in-page-layout/sign-in-page-layout.js';
14
14
 
15
- // TODO: We need larger images
16
- const IMAGE = {
17
- 1: 'https://res.cloudinary.com/dkz9eknwh/image/upload/w_1658,h_1008/v1740665245/images/sign-in-page.webp',
18
- 2: 'https://res.cloudinary.com/dkz9eknwh/image/upload/w_1658,h_1008/v1740665245/images/sign-in-page.webp',
19
- 3: 'https://res.cloudinary.com/dkz9eknwh/image/upload/w_1658,h_1008/v1740665245/images/sign-in-page.webp',
20
- altText: '',
21
- };
22
15
  function SignInPage({ returnUrl } = {}) {
23
16
  const { navigate } = useNavigate();
24
17
  const [isSuccess, setIsSuccess] = useState(false);
@@ -26,7 +19,7 @@ function SignInPage({ returnUrl } = {}) {
26
19
  const { data: session } = useFetchSession();
27
20
  const { error: errorSignIn, isPending: isPendingSignIn, mutate: signIn, reset: resetSignIn, } = useSignIn();
28
21
  const { error: errorCreateGuest, isPending: isPendingCreateGuest, mutate: createGuest, reset: resetCreateGuest, } = useCreateGuestAccount();
29
- const errorMessage = useMemo(() => {
22
+ const errorType = useMemo(() => {
30
23
  const error = errorSignIn || errorCreateGuest;
31
24
  if (!error)
32
25
  return undefined;
@@ -63,7 +56,7 @@ function SignInPage({ returnUrl } = {}) {
63
56
  const onRecoverPasswordDialogOpen = () => {
64
57
  setRecoverPasswordDialogOpen(true);
65
58
  };
66
- return (jsxs(Fragment, { children: [jsx(Page, { fullHeight: true, "data-test-selector": "signInPage", fluid: true, children: jsx(SignInPageLayout, { fullHeight: true, image: IMAGE, children: jsx(SignInForm, { allowGuestSignIn: allowGuestSignIn, createAccountPath: createAccountPath, errorMessage: errorMessage, initialEmail: session?.isGuest ? '' : session?.email, initialRememberMe: session?.rememberMe, isDisabled: !session || isSuccess, isPendingGuestSignIn: isPendingCreateGuest, isPendingUserSignIn: isPendingSignIn, onRecoverPasswordDialogOpen: onRecoverPasswordDialogOpen, onSubmit: onSubmit }) }) }), jsx(RecoverPasswordDialog, { isOpen: isRecoverPasswordDialogOpen, onOpenChange: isOpen => setRecoverPasswordDialogOpen(isOpen) })] }));
59
+ return (jsxs(Fragment, { children: [jsx(Page, { fullHeight: true, "data-test-selector": "signInPage", fluid: true, children: jsx(SignInPageLayout, { fullHeight: true, children: jsx(SignInForm, { allowGuestSignIn: allowGuestSignIn, createAccountPath: createAccountPath, errorType: errorType, initialEmail: session?.isGuest ? '' : session?.email, initialRememberMe: session?.rememberMe, isDisabled: !session || isSuccess, isPendingGuestSignIn: isPendingCreateGuest, isPendingUserSignIn: isPendingSignIn, onRecoverPasswordDialogOpen: onRecoverPasswordDialogOpen, onSubmit: onSubmit }) }) }), jsx(RecoverPasswordDialog, { isOpen: isRecoverPasswordDialogOpen, onOpenChange: isOpen => setRecoverPasswordDialogOpen(isOpen) })] }));
67
60
  }
68
61
 
69
62
  export { SignInPage };
@@ -55,7 +55,7 @@ function OrderConfirmationPageContent({ cart, }) {
55
55
  secondary: (jsxs(Fragment, { children: [cart.canSaveOrder && (jsx(RouteButton, { color: "secondary", onClick: () => {
56
56
  saveCartForLater.mutate({ cart });
57
57
  }, variant: "outline", children: jsx(FormattedMessage, { id: "Save order" }) })), jsx(PrintButton, {})] })),
58
- }, overview: jsx(CartTotals, { currencyCode: currencyCode, fulfillmentMethod: t(`fulfillmentmethod.${cart.fulfillmentMethod}`), orderNumber: cart.orderNumber, shippingCost: cart.shippingAndHandling, subtotal: cart.orderSubTotal, tax: cart.totalTax, total: cart.orderGrandTotal, vatPercentage: cart.cartLines?.[0]?.pricing?.vatRate }), children: jsxs("div", { children: [jsx(CheckoutPageSection, { hasBorder: false, title: t('General'), children: jsx(CheckoutPageSectionContent, { children: jsxs("div", { className: styles['general-order-info'], children: [cart.orderDate && (jsx(InfoDisplay, { id: "order-date", label: t('Order date'), value: formatDateToLocaleString(new Date(cart.orderDate), cultureCode) })), cart.requestedDeliveryDateDisplay && (jsx(InfoDisplay, { id: "requested-delivery-date", label: t('Requested delivery date'), value: formatDateToLocaleString(new Date(cart.requestedDeliveryDateDisplay.toString()), cultureCode) })), cart.poNumber && (jsx(InfoDisplay, { id: "po-number", label: t('PO Number'), value: cart.poNumber }))] }) }) }), jsx(CheckoutPageSection, { hasBorder: false, title: t('Billing and shipping information'), children: jsx(CheckoutPageSectionContent, { children: jsx(BillingAndInvoiceInformation, { billToAddress: {
58
+ }, overview: jsx(CartTotals, { currencyCode: currencyCode, fulfillmentMethod: cart.fulfillmentMethod, orderNumber: cart.orderNumber, shippingCost: cart.shippingAndHandling, subtotal: cart.orderSubTotal, tax: cart.totalTax, total: cart.orderGrandTotal, vatPercentage: cart.cartLines?.[0]?.pricing?.vatRate }), children: jsxs("div", { children: [jsx(CheckoutPageSection, { hasBorder: false, title: t('General'), children: jsx(CheckoutPageSectionContent, { children: jsxs("div", { className: styles['general-order-info'], children: [cart.orderDate && (jsx(InfoDisplay, { id: "order-date", label: t('Order date'), value: formatDateToLocaleString(new Date(cart.orderDate), cultureCode) })), cart.requestedDeliveryDateDisplay && (jsx(InfoDisplay, { id: "requested-delivery-date", label: t('Requested delivery date'), value: formatDateToLocaleString(new Date(cart.requestedDeliveryDateDisplay.toString()), cultureCode) })), cart.poNumber && (jsx(InfoDisplay, { id: "po-number", label: t('PO Number'), value: cart.poNumber }))] }) }) }), jsx(CheckoutPageSection, { hasBorder: false, title: t('Billing and shipping information'), children: jsx(CheckoutPageSectionContent, { children: jsx(BillingAndInvoiceInformation, { billToAddress: {
59
59
  address1: cart.billTo?.address1,
60
60
  address2: cart.billTo?.address2,
61
61
  address3: cart.billTo?.address3,
@@ -33,6 +33,7 @@ function Payment({ atp, cart: _cart, form, isProcessing, onError: _onError, onPa
33
33
  const { sendPurchaseEventFromPaymentPage } = useAlgoliaInsights();
34
34
  const invalidateCurrentCart = useInvalidateCurrentCart();
35
35
  const dropinRef = useRef(null);
36
+ const hasReturnedFromAdyen = useHasReturnedFromAdyen();
36
37
  const [paymentError, setPaymentError] = useState();
37
38
  const [apiError, setAPIError] = useState();
38
39
  const invalidateAdyen = useInvalidateAdyen();
@@ -40,7 +41,7 @@ function Payment({ atp, cart: _cart, form, isProcessing, onError: _onError, onPa
40
41
  const cartRef = useRef(_cart);
41
42
  const cart = cartRef.current;
42
43
  const hasAtp = atp.length > 1;
43
- const [asSoonAsPossible, setAsSoonAsPossible] = useState(!hasAtp);
44
+ const [asSoonAsPossible, setAsSoonAsPossible] = useState(!hasAtp || (hasReturnedFromAdyen && !cart.requestedDeliveryDate));
44
45
  const [deliveryDate, setDeliveryDate] = useState(cart.requestedDeliveryDateDisplay?.toString() || '');
45
46
  const [selectedPaymentMethod, setSelectedPaymentMethod] = useState(cart.paymentOptions?.paymentMethods?.[0]?.name || 'ADY');
46
47
  if (!isCountryCode(_cart.billTo?.country?.abbreviation))
@@ -54,7 +55,6 @@ function Payment({ atp, cart: _cart, form, isProcessing, onError: _onError, onPa
54
55
  cart.paymentOptions &&
55
56
  cart.billTo?.id &&
56
57
  countryCode;
57
- const hasReturnedFromAdyen = useHasReturnedFromAdyen();
58
58
  const isDisabled = isProcessing;
59
59
  useEffect(() => {
60
60
  if (!hasReturnedFromAdyen)
@@ -187,6 +187,7 @@ function Payment({ atp, cart: _cart, form, isProcessing, onError: _onError, onPa
187
187
  industry: formData.get('industry')?.toString() || '',
188
188
  };
189
189
  cart.requestedDeliveryDate = formData.get('deliveryDate')?.toString();
190
+ cart.requestedDeliveryDateDisplay = undefined;
190
191
  if (cart.customerVatNumber &&
191
192
  lastVATNumber.current !== cart.customerVatNumber &&
192
193
  !(await validateVAT(cart.customerVatNumber)))
@@ -299,11 +300,11 @@ function Payment({ atp, cart: _cart, form, isProcessing, onError: _onError, onPa
299
300
  return (jsxs(Form, { className: styles['payment-form'], "data-test-selector": "paymentForm", id: form, onSubmit: e => {
300
301
  e.preventDefault();
301
302
  onSubmit(e);
302
- }, validationErrors: validationErrors, children: [Boolean(apiError) && (jsx("div", { className: styles['error-message'], children: jsx(FormattedMessage, { id: "An unexpected error occured" }) })), hasAtp && (jsxs("div", { className: styles['delivery-date'], children: [jsx(Select, { showLabel: true, "data-test-selector": "deliveryDateSelect", isDisabled: isDisabled || asSoonAsPossible, isRequired: !asSoonAsPossible, label: t('Select a desired delivery date'), name: "deliveryDate", onChange: setDeliveryDate, options: atpSelectOptions, selectedOption: deliveryDate, variant: "solid" }, String(asSoonAsPossible)), jsxs("div", { className: styles['asap-checkbox'], children: [jsx(Checkbox, { "data-test-selector": "asapCheckbox", isDisabled: isDisabled, isSelected: asSoonAsPossible, onChange: checked => {
303
+ }, validationErrors: validationErrors, children: [Boolean(apiError) && (jsx("div", { className: styles['error-message'], children: jsx(FormattedMessage, { id: "An unexpected error occured" }) })), hasAtp && (jsxs("div", { className: styles['delivery-date'], children: [jsx(Select, { showLabel: true, "data-test-selector": "deliveryDateSelect", defaultSelectedOption: deliveryDate, isDisabled: isDisabled || asSoonAsPossible, isRequired: !asSoonAsPossible, label: t('Select a desired delivery date'), name: "deliveryDate", onChange: setDeliveryDate, options: atpSelectOptions, selectedOption: deliveryDate, variant: "solid" }, String(asSoonAsPossible)), jsxs("div", { className: styles['asap-checkbox'], children: [jsx(Checkbox, { "data-test-selector": "asapCheckbox", isDisabled: isDisabled || Boolean(cart.requestedDeliveryDateDisplay), isSelected: asSoonAsPossible, onChange: checked => {
303
304
  setAsSoonAsPossible(checked);
304
305
  if (checked)
305
306
  setDeliveryDate('');
306
- }, children: jsx(FormattedMessage, { id: "As soon as possible" }) }), jsx(InfoIconTooltip, { variant: "stroke", children: t('Selecting As Soon As Possible will enable us to send the products to you as they become available.') })] })] })), jsx(Select, { isRequired: true, "data-test-selector": "industrySelect", defaultSelectedOption: cart.properties.industry, isDisabled: isDisabled, label: t('Industry'), name: "industry", options: {
307
+ }, children: t('As soon as possible') }), jsx(InfoIconTooltip, { variant: "stroke", children: t('Selecting As Soon As Possible will enable us to send the products to you as they become available.') })] })] })), jsx(Select, { isRequired: true, "data-test-selector": "industrySelect", defaultSelectedOption: cart.properties.industry, isDisabled: isDisabled, label: t('Industry'), name: "industry", options: {
307
308
  /* eslint-disable sort-keys-fix/sort-keys-fix */
308
309
  PP: 'Private User',
309
310
  AU: 'Automotive',
@@ -315,7 +316,7 @@ function Payment({ atp, cart: _cart, form, isProcessing, onError: _onError, onPa
315
316
  MA: 'Maritime',
316
317
  OT: 'Other',
317
318
  /* eslint-enable sort-keys-fix/sort-keys-fix */
318
- }, variant: "solid" }), jsx(TextField, { showLabel: true, isDisabled: isDisabled, label: t('VAT Number'), name: "customerVatNumber", onBlur: e => validateVAT(e.target.value), onChange: setCustomerVatNumber, validate: () => validationErrors.customerVatNumber ?? true, value: customerVatNumber }, `vat${Boolean(validationErrors.customerVatNumber)}`), jsx(TextField, { showLabel: true, defaultValue: cart.poNumber, isDisabled: isDisabled, isRequired: cart.requiresPoNumber, label: t('PO Number'), name: "poNumber" }), paymentMethodOptions && Object.keys(paymentMethodOptions).length > 1 && (jsx(Select, { "data-test-selector": "paymentMethodSelect", defaultSelectedOption: cart.paymentOptions?.paymentMethods?.[0]?.name || 'ADY', isDisabled: isDisabled, label: t('Payment method'), name: "paymentMethod", onChange: setSelectedPaymentMethod, options: paymentMethodOptions, selectedOption: selectedPaymentMethod, variant: "solid" })), isAdyenPayment && cart.billTo && (jsx(AdyenPayment, { amount: cart.orderGrandTotal, cartId: cart.trackId, countryCode: countryCode, currencyCode: currencyCode, customerId: cart.billTo.id, dropinRef: dropinRef, environment: environment === 'production' ? 'live' : 'test', isDisabled: isDisabled, onComplete: onComplete, onError: onError, orderAmount: cart.orderGrandTotal, returnUrl:
319
+ }, placeholder: t('Select an industry'), variant: "solid" }), jsx(TextField, { showLabel: true, isDisabled: isDisabled, label: t('VAT Number'), name: "customerVatNumber", onBlur: e => validateVAT(e.target.value), onChange: setCustomerVatNumber, validate: () => validationErrors.customerVatNumber ?? true, value: customerVatNumber }, `vat${Boolean(validationErrors.customerVatNumber)}`), jsx(TextField, { showLabel: true, defaultValue: cart.poNumber, isDisabled: isDisabled, isRequired: cart.requiresPoNumber, label: t('PO Number'), name: "poNumber" }), paymentMethodOptions && Object.keys(paymentMethodOptions).length > 1 && (jsx(Select, { "data-test-selector": "paymentMethodSelect", defaultSelectedOption: cart.paymentOptions?.paymentMethods?.[0]?.name || 'ADY', isDisabled: isDisabled, label: t('Payment method'), name: "paymentMethod", onChange: setSelectedPaymentMethod, options: paymentMethodOptions, selectedOption: selectedPaymentMethod, variant: "solid" })), isAdyenPayment && cart.billTo && (jsx(AdyenPayment, { amount: cart.orderGrandTotal, cartId: cart.trackId, countryCode: countryCode, currencyCode: currencyCode, customerId: cart.billTo.id, dropinRef: dropinRef, environment: environment === 'production' ? 'live' : 'test', isDisabled: isDisabled, onComplete: onComplete, onError: onError, orderAmount: cart.orderGrandTotal, returnUrl:
319
320
  /* eslint-disable ssr-friendly/no-dom-globals-in-react-fc */
320
321
  typeof window === 'undefined'
321
322
  ? ''
@@ -18,7 +18,7 @@ function ShippingPageContent({ cart, editAddress, errorPatchBillingAddress, fulf
18
18
  const t = useFormattedMessage();
19
19
  const fulfillmentMethodOptions = fulfillmentMethods?.reduce((acc, method) => ({
20
20
  ...acc,
21
- [method]: t(`fulfillmentMethod.${method}`),
21
+ [method]: t(`fulfillmentmethod.${method}`),
22
22
  }), {});
23
23
  const hasBillToAddress = Boolean(cart.billTo?.address1);
24
24
  const currencyCode = getCurrencyCodeBySymbol(cart.currencySymbol);
@@ -39,7 +39,7 @@ function ShippingPage() {
39
39
  const isAuthenticated = session?.isAuthenticated;
40
40
  const isLoading = isLoadingCart || isLoadingCountries || isLoadingFulfillmentMethods;
41
41
  const { isNavigating, navigate } = useNavigate();
42
- const isPickup = cart?.fulfillmentMethod === 'PickUp';
42
+ const isPickup = Boolean(cart?.fulfillmentMethod.match(/pickup/i));
43
43
  useEffect(() => {
44
44
  /* Initial guards. When these are not met, we should not proceed */
45
45
  if (isNavigating)
@@ -0,0 +1,3 @@
1
+ import { AccountModel } from 'shared/api/storefront/model/storefront.model';
2
+ import { CreateAccountRequestBody } from 'shared/api/storefront/services/authentication-service';
3
+ export declare function useCreateAccount(): import("@tanstack/react-query").UseMutationResult<AccountModel, Error, CreateAccountRequestBody, unknown>;
@@ -0,0 +1,28 @@
1
+ import { useQueryClient, useMutation } from '@tanstack/react-query';
2
+ import { createAccount, createSession } from '../../services/authentication-service.js';
3
+
4
+ function useCreateAccount() {
5
+ const queryClient = useQueryClient();
6
+ return useMutation({
7
+ mutationFn: async (account) => {
8
+ try {
9
+ const body = await createAccount(account);
10
+ const updatedSession = await createSession({
11
+ accessToken: '',
12
+ isGuest: false,
13
+ password: account.password,
14
+ rememberMe: false,
15
+ userName: body.userName,
16
+ });
17
+ queryClient.setQueriesData({ exact: true, queryKey: ['session'] }, updatedSession);
18
+ return body;
19
+ }
20
+ catch (error) {
21
+ queryClient.resetQueries();
22
+ throw error;
23
+ }
24
+ },
25
+ });
26
+ }
27
+
28
+ export { useCreateAccount };
@@ -1,3 +1,4 @@
1
+ import { RequestError } from '../../../fetch/request';
1
2
  import { AccountModel, PatchSessionModel, SessionModel } from '../model/storefront.model';
2
3
  export declare function fetchSession(): Promise<SessionModel>;
3
4
  export declare function patchSession({ session, }: {
@@ -31,3 +32,25 @@ export declare function createGuestAccount({ defaultWarehouseId, }: {
31
32
  export declare function recoverPassword({ userName, }: {
32
33
  userName: string;
33
34
  }): Promise<SessionModel>;
35
+ export declare class ExistingAccountError extends RequestError {
36
+ constructor(error: Error);
37
+ }
38
+ export interface CreateAccountRequestBase {
39
+ email: string;
40
+ firstName?: string;
41
+ isSubscribed: boolean;
42
+ lastName: string;
43
+ password: string;
44
+ }
45
+ export interface CreatePrivateAccountRequestBody extends CreateAccountRequestBase {
46
+ companyName?: string | undefined;
47
+ countryCode: string;
48
+ isPrivateAccount: true;
49
+ }
50
+ export interface CreateBusinessAccountRequestBody extends CreateAccountRequestBase {
51
+ companyName: string;
52
+ countryCode: string;
53
+ isPrivateAccount: false;
54
+ }
55
+ export type CreateAccountRequestBody = CreatePrivateAccountRequestBody | CreateBusinessAccountRequestBody;
56
+ export declare function createAccount({ companyName, countryCode, email, firstName, isPrivateAccount, isSubscribed, lastName, password, }: CreateAccountRequestBody): Promise<AccountModel>;
@@ -1,5 +1,5 @@
1
1
  import { config } from '../../../../config.js';
2
- import { request } from '../../../fetch/request.js';
2
+ import { request, RequestError, isRequestError } from '../../../fetch/request.js';
3
3
 
4
4
  async function fetchSession() {
5
5
  const { body } = await request({
@@ -92,5 +92,43 @@ async function recoverPassword({ userName, }) {
92
92
  });
93
93
  return body;
94
94
  }
95
+ class ExistingAccountError extends RequestError {
96
+ constructor(error) {
97
+ super(error);
98
+ this.name = 'ExistingAccountError';
99
+ }
100
+ }
101
+ async function createAccount({ companyName, countryCode, email, firstName, isPrivateAccount, isSubscribed, lastName, password, }) {
102
+ try {
103
+ const { body } = await request({
104
+ body: {
105
+ email,
106
+ firstName,
107
+ isSubscribed,
108
+ lastName,
109
+ password,
110
+ properties: {
111
+ PrivateAccount: isPrivateAccount,
112
+ companyName,
113
+ country: countryCode,
114
+ },
115
+ userName: '',
116
+ },
117
+ headers: {
118
+ 'Content-Type': 'application/json',
119
+ },
120
+ method: 'POST',
121
+ url: `${config.SHOP_API_URL}/api/v1/accounts`,
122
+ });
123
+ return body;
124
+ }
125
+ catch (error) {
126
+ if (isRequestError(error) &&
127
+ error.status === 400 &&
128
+ error.body?.message === 'Email Address already exists')
129
+ throw new ExistingAccountError(error);
130
+ throw error;
131
+ }
132
+ }
95
133
 
96
- export { createGuestAccount, createSession, fetchSession, patchSession, recoverPassword, signIn, signOut };
134
+ export { ExistingAccountError, createAccount, createGuestAccount, createSession, fetchSession, patchSession, recoverPassword, signIn, signOut };
@@ -1,13 +1,13 @@
1
- interface UseScriptCodeBaseProps {
1
+ interface UseScriptCodePropsBase {
2
2
  async?: boolean;
3
3
  enabled?: boolean;
4
4
  type?: 'module' | 'text/javascript';
5
5
  }
6
- interface UseScriptCodeProps extends UseScriptCodeBaseProps {
6
+ interface UseScriptCodeProps extends UseScriptCodePropsBase {
7
7
  code: string;
8
8
  nonce?: string;
9
9
  }
10
- interface UseScriptUrlProps extends UseScriptCodeBaseProps {
10
+ interface UseScriptUrlProps extends UseScriptCodePropsBase {
11
11
  src: string;
12
12
  }
13
13
  type UseScriptProps = UseScriptUrlProps | UseScriptCodeProps;
@@ -0,0 +1,19 @@
1
+ export declare const validatePassword: (value: string) => boolean;
2
+ export interface AccountBase {
3
+ email: string;
4
+ firstName?: string;
5
+ isSubscribed: boolean;
6
+ lastName: string;
7
+ password: string;
8
+ }
9
+ export interface PrivateAccount extends AccountBase {
10
+ companyName?: string | undefined;
11
+ countryCode: string;
12
+ isPrivateAccount: true;
13
+ }
14
+ export interface BusinessAccount extends AccountBase {
15
+ companyName: string;
16
+ countryCode: string;
17
+ isPrivateAccount: false;
18
+ }
19
+ export type Account = PrivateAccount | BusinessAccount;
@@ -0,0 +1,4 @@
1
+ const passwordRequirementsRegExp = new RegExp(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*\W).{8,}$/);
2
+ const validatePassword = (value) => passwordRequirementsRegExp.test(value);
3
+
4
+ export { validatePassword };