@sonic-equipment/ui 140.0.0 → 141.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.
- package/dist/exports.d.ts +2 -0
- package/dist/forms/text-field/text-field.d.ts +3 -2
- package/dist/forms/text-field/text-field.js +2 -2
- package/dist/icons/stroke/stroke-closebox-icon.js +1 -1
- package/dist/index.js +3 -2
- package/dist/intl/translation-id.d.ts +1 -1
- package/dist/modals/dialog/dialog.js +3 -1
- package/dist/modals/recover-password/recover-password-dialog.d.ts +6 -0
- package/dist/modals/recover-password/recover-password-dialog.js +37 -0
- package/dist/modals/signin/sign-in-dialog.js +2 -3
- package/dist/pages/account/sign-in-page/sign-in-page.js +9 -2
- package/dist/pages/checkout/payment-page/components/payment.d.ts +1 -2
- package/dist/pages/checkout/payment-page/components/payment.js +28 -16
- package/dist/pages/checkout/payment-page/payment-page-content.js +1 -1
- package/dist/pages/checkout/shipping-page/components/edit-address-form.js +1 -1
- package/dist/pages/paths.d.ts +1 -0
- package/dist/pages/paths.js +1 -0
- package/dist/shared/api/storefront/hooks/authentication/use-recover-password.d.ts +4 -0
- package/dist/shared/api/storefront/hooks/authentication/use-recover-password.js +15 -0
- package/dist/shared/api/storefront/services/authentication-service.d.ts +6 -3
- package/dist/shared/api/storefront/services/authentication-service.js +15 -1
- package/dist/shared/fetch/request.d.ts +15 -14
- package/dist/sign-in-form/sign-in-form.d.ts +3 -1
- package/dist/sign-in-form/sign-in-form.js +5 -4
- package/dist/styles.css +5 -5
- package/package.json +3 -3
- package/dist/modals/signin/sign-in-dialog.module.css.js +0 -3
package/dist/exports.d.ts
CHANGED
|
@@ -143,6 +143,7 @@ export * from './modals/confirmation/confirmation-dialog';
|
|
|
143
143
|
export * from './modals/dialog/dialog';
|
|
144
144
|
export * from './modals/favorite/add-to-favorite-dialog';
|
|
145
145
|
export * from './modals/modal/modal';
|
|
146
|
+
export * from './modals/recover-password/recover-password-dialog';
|
|
146
147
|
export * from './modals/signin/sign-in-dialog';
|
|
147
148
|
export * from './notifications/announcements/announcement';
|
|
148
149
|
export * from './notifications/announcements/announcement-provider';
|
|
@@ -209,6 +210,7 @@ export * from './shared/api/storefront/hooks/authentication/use-fetch-session';
|
|
|
209
210
|
export * from './shared/api/storefront/hooks/authentication/use-invalidate-session';
|
|
210
211
|
export * from './shared/api/storefront/hooks/authentication/use-is-authenticated';
|
|
211
212
|
export * from './shared/api/storefront/hooks/authentication/use-patch-session';
|
|
213
|
+
export * from './shared/api/storefront/hooks/authentication/use-recover-password';
|
|
212
214
|
export * from './shared/api/storefront/hooks/authentication/use-sign-in';
|
|
213
215
|
export * from './shared/api/storefront/hooks/authentication/use-sign-out';
|
|
214
216
|
export * from './shared/api/storefront/hooks/cart/use-add-product-to-current-cart';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FormEventHandler, type KeyboardEvent } from 'react';
|
|
1
|
+
import { FocusEvent, type FormEventHandler, type KeyboardEvent } from 'react';
|
|
2
2
|
import { type ValidateFunction } from '../field-error/field-error';
|
|
3
3
|
interface TextFieldProps {
|
|
4
4
|
autoFocus?: boolean;
|
|
@@ -15,6 +15,7 @@ interface TextFieldProps {
|
|
|
15
15
|
maxLength?: number;
|
|
16
16
|
minLength?: number;
|
|
17
17
|
name?: string;
|
|
18
|
+
onBlur?: (e: FocusEvent<HTMLElement>) => void;
|
|
18
19
|
onChange?: (value: string) => void;
|
|
19
20
|
onInput?: FormEventHandler<HTMLInputElement>;
|
|
20
21
|
onKeyUp?: (e: KeyboardEvent) => void;
|
|
@@ -31,5 +32,5 @@ interface TextFieldProps {
|
|
|
31
32
|
* It can be used as a single line input or as a textarea.
|
|
32
33
|
* This field can also grow when a user types in text.
|
|
33
34
|
*/
|
|
34
|
-
export declare function TextField({ autoFocus, autoGrow, 'data-test-selector': dataTestSelector, defaultValue, info, isDisabled, isInvalid, isMultiline, isReadOnly, isRequired, label, maxLength, minLength, name, onChange, onInput, onKeyUp, placeholder, rows, showLabel, size, type: defaultType, validate, value, }: TextFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
export declare function TextField({ autoFocus, autoGrow, 'data-test-selector': dataTestSelector, defaultValue, info, isDisabled, isInvalid, isMultiline, isReadOnly, isRequired, label, maxLength, minLength, name, onBlur, onChange, onInput, onKeyUp, placeholder, rows, showLabel, size, type: defaultType, validate, value, }: TextFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
35
36
|
export {};
|
|
@@ -16,7 +16,7 @@ import styles from './text-field.module.css.js';
|
|
|
16
16
|
* It can be used as a single line input or as a textarea.
|
|
17
17
|
* This field can also grow when a user types in text.
|
|
18
18
|
*/
|
|
19
|
-
function TextField({ autoFocus, autoGrow, 'data-test-selector': dataTestSelector, defaultValue, info, isDisabled, isInvalid, isMultiline, isReadOnly, isRequired, label, maxLength, minLength, name, onChange, onInput, onKeyUp, placeholder, rows, showLabel = false, size = 'lg', type: defaultType = 'text', validate, value, }) {
|
|
19
|
+
function TextField({ autoFocus, autoGrow, 'data-test-selector': dataTestSelector, defaultValue, info, isDisabled, isInvalid, isMultiline, isReadOnly, isRequired, label, maxLength, minLength, name, onBlur, onChange, onInput, onKeyUp, placeholder, rows, showLabel = false, size = 'lg', type: defaultType = 'text', validate, value, }) {
|
|
20
20
|
const [type, setType] = useState(defaultType);
|
|
21
21
|
const isPasswordInput = type === 'password';
|
|
22
22
|
const togglePasswordType = useCallback(() => {
|
|
@@ -24,7 +24,7 @@ function TextField({ autoFocus, autoGrow, 'data-test-selector': dataTestSelector
|
|
|
24
24
|
}, [isPasswordInput]);
|
|
25
25
|
return (jsxs(TextField$1, { "aria-label": label, autoFocus: autoFocus, className: clsx(styles.field, styles[size]), "data-info": info ? true : undefined, defaultValue: defaultValue, isDisabled: isDisabled, isInvalid: isInvalid, isReadOnly: isReadOnly, isRequired: isRequired, maxLength: maxLength, minLength: minLength, name: name, onChange: value => {
|
|
26
26
|
onChange?.(value);
|
|
27
|
-
}, onInput: onInput, onKeyUp: e => onKeyUp?.(e), validate: validate, value: value, children: [showLabel && (jsx(Label, { className: styles['label'], "data-test-selector": dataTestSelector ? `${dataTestSelector}-label` : undefined, isRequired: isRequired, children: label })), isMultiline ? (jsx(TextArea, { autoGrow: autoGrow, className: styles['input'], "data-test-selector": dataTestSelector, label: label, placeholder: placeholder, rows: rows, size: size })) : (jsx(Input, { autoGrow: autoGrow, className: styles['input'], "data-test-selector": dataTestSelector, inlineElement: defaultType === 'password' && (jsx(PasswordRevealToggle, { onChange: togglePasswordType, showPassword: !isPasswordInput })), label: label, placeholder: placeholder, size: size, type: type })), info && (jsx(InfoIconTooltip, { className: styles['info'], children: info })), jsx(FieldError, { className: styles['error'] }, Math.random())] }));
|
|
27
|
+
}, onInput: onInput, onKeyUp: e => onKeyUp?.(e), validate: validate, value: value, children: [showLabel && (jsx(Label, { className: styles['label'], "data-test-selector": dataTestSelector ? `${dataTestSelector}-label` : undefined, isRequired: isRequired, children: label })), isMultiline ? (jsx(TextArea, { autoGrow: autoGrow, className: styles['input'], "data-test-selector": dataTestSelector, label: label, onBlur: onBlur, placeholder: placeholder, rows: rows, size: size })) : (jsx(Input, { autoGrow: autoGrow, className: styles['input'], "data-test-selector": dataTestSelector, inlineElement: defaultType === 'password' && (jsx(PasswordRevealToggle, { onChange: togglePasswordType, showPassword: !isPasswordInput })), label: label, onBlur: onBlur, placeholder: placeholder, size: size, type: type })), info && (jsx(InfoIconTooltip, { className: styles['info'], children: info })), jsx(FieldError, { className: styles['error'] }, Math.random())] }));
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export { TextField };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
|
|
3
3
|
function StrokeCloseboxIcon(props) {
|
|
4
|
-
return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", ...props, height: "24", viewBox: "0 0 24 24", width: "24", children: jsx("path", { d: "M8,8 L16,16 M16,8 L8,16", fill: "currentColor", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.
|
|
4
|
+
return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", ...props, height: "24", viewBox: "0 0 24 24", width: "24", children: jsx("path", { d: "M8,8 L16,16 M16,8 L8,16", fill: "currentColor", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.5" }) }));
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export { StrokeCloseboxIcon };
|
package/dist/index.js
CHANGED
|
@@ -146,6 +146,7 @@ export { ConfirmationDialog } from './modals/confirmation/confirmation-dialog.js
|
|
|
146
146
|
export { Dialog } from './modals/dialog/dialog.js';
|
|
147
147
|
export { AddToFavoriteDialog } from './modals/favorite/add-to-favorite-dialog.js';
|
|
148
148
|
export { Modal } from './modals/modal/modal.js';
|
|
149
|
+
export { RecoverPasswordDialog } from './modals/recover-password/recover-password-dialog.js';
|
|
149
150
|
export { SignInDialog } from './modals/signin/sign-in-dialog.js';
|
|
150
151
|
export { Announcement } from './notifications/announcements/announcement.js';
|
|
151
152
|
export { AnnouncementProvider } from './notifications/announcements/announcement-provider.js';
|
|
@@ -211,6 +212,7 @@ export { useFetchSession } from './shared/api/storefront/hooks/authentication/us
|
|
|
211
212
|
export { useInvalidateSession } from './shared/api/storefront/hooks/authentication/use-invalidate-session.js';
|
|
212
213
|
export { useIsAuthenticated } from './shared/api/storefront/hooks/authentication/use-is-authenticated.js';
|
|
213
214
|
export { usePatchSession } from './shared/api/storefront/hooks/authentication/use-patch-session.js';
|
|
215
|
+
export { useRecoverPassword } from './shared/api/storefront/hooks/authentication/use-recover-password.js';
|
|
214
216
|
export { useSignIn } from './shared/api/storefront/hooks/authentication/use-sign-in.js';
|
|
215
217
|
export { useSignOut } from './shared/api/storefront/hooks/authentication/use-sign-out.js';
|
|
216
218
|
export { useAddProductToCurrentCart } from './shared/api/storefront/hooks/cart/use-add-product-to-current-cart.js';
|
|
@@ -250,7 +252,7 @@ export { useCreateWishList } from './shared/api/storefront/hooks/wishlist/use-cr
|
|
|
250
252
|
export { useDeleteWishListItemFromWishList } from './shared/api/storefront/hooks/wishlist/use-delete-wishlist-item-from-wishlist.js';
|
|
251
253
|
export { useFetchAllWishListsItems } from './shared/api/storefront/hooks/wishlist/use-fetch-all-wishlists-items.js';
|
|
252
254
|
export { useFetchWishLists } from './shared/api/storefront/hooks/wishlist/use-fetch-wishlists.js';
|
|
253
|
-
export { createGuestAccount, createSession, fetchSession, patchSession, signIn, signOut } from './shared/api/storefront/services/authentication-service.js';
|
|
255
|
+
export { createGuestAccount, createSession, fetchSession, patchSession, recoverPassword, signIn, signOut } from './shared/api/storefront/services/authentication-service.js';
|
|
254
256
|
export { addProductToCurrentCart, convertToMinorUnits, deleteCartLineById, deleteCurrentCart, fetchCart, fetchCurrentCart, fetchCurrentCartLines, fetchCurrentCartProductAtp, fetchCurrentCartPromotions, fetchCurrentCheckoutAtp, getAdyenPaymentDetails, patchCart, patchCartLineById, placeOrder, postAdyenPayment, saveCartForLater } from './shared/api/storefront/services/cart-service.js';
|
|
255
257
|
export { fetchBillToAddresses, fetchFulfillmentMethods, fetchShipToAddresses, patchBillToAddress } from './shared/api/storefront/services/customer-service.js';
|
|
256
258
|
export { validateVATNumber } from './shared/api/storefront/services/finance-service.js';
|
|
@@ -323,7 +325,6 @@ export { getTokenKeyValue, getTokenValue } from './tokens/tokens.js';
|
|
|
323
325
|
export { Tooltip } from './tooltip/tooltip.js';
|
|
324
326
|
export { Heading, SIZES, TAGS } from './typography/heading/heading.js';
|
|
325
327
|
|
|
326
|
-
/* eslint-disable simple-import-sort/exports */
|
|
327
328
|
// eslint-disable-next-line no-console
|
|
328
329
|
console.log(`@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
329
330
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type TranslationId = "'{0}' in all products" | "Try 'Search' and try to find the product you're looking for" | "Unfortnately, We found no articles for your search '{0}'" | ' to your account to manage your lists.' | 'Access denied.' | 'Your email and password were not recognized.' | 'Add order notes' | 'Add to list' | 'Address' | 'Amount: {0}' | 'An error occurred while processing your payment. Please try again.' | 'An unexpected error occured' | 'An unexpected error occured. Please try again.' | 'Are you looking for information about our service? Please visit our customer support page' | 'Are you sure you want to remove all items from your cart?' | 'Are you sure you want to remove this item from your cart?' | 'article' | 'articles' | 'As soon as possible' | 'Attention' | 'Billing address' | 'Billing and shipping address' | 'Billing and shipping information' | 'Cancel' | 'Cart' | 'Changing your address is currently not possible. Please contact customer support to change your address.' | 'Chosen filters' | 'City' | 'Clear filters' | 'Clear' | 'Click the button below to continue shopping.' | 'Company name' | 'Conceal value' | 'Continue shopping' | 'Continue' | 'Cost overview' | 'Country' | 'create account' | 'Create new list' | 'Delivery date' | 'Delivery expected in {0} {1}' | 'Double check your spelling' | 'Downloads' | 'Easily add your favorite products' | 'Edit billing address' | 'Edit shipping address' | 'Email' | 'Excl. VAT' | 'Explore by categories' | 'Exploring our products by category' | 'facet.categories' | 'facet.height' | 'facet.weight' | 'Features' | 'First name' | 'Forgot password?' | 'Fulfillment method' | 'General' | 'Hide filters' | 'Home' | 'Incl. VAT' | 'Includes' | 'Industry' | 'Information' | 'Language' | 'Last name' | 'List name already exists' | 'New list name' | 'New user?' | 'of' | 'Or continue as guest' | 'Order number' | 'Order' | 'Order confirmation' | 'Order date' | 'Order number' | 'Order' | 'Pay by invoice' | 'Pay' | 'Payment method' | 'Payment' | 'Password' | 'pc' | 'Phone' | 'Pick up' | 'Pickup address' | 'Please enter a valid
|
|
1
|
+
export type TranslationId = "'{0}' in all products" | "Try 'Search' and try to find the product you're looking for" | "Unfortnately, We found no articles for your search '{0}'" | ' to your account to manage your lists.' | 'Access denied.' | 'Your email and password were not recognized.' | 'Add order notes' | 'Add to list' | 'Address' | 'Amount: {0}' | 'An error occurred while processing your payment. Please try again.' | 'An unexpected error occured' | 'An unexpected error occured. Please try again.' | 'Are you looking for information about our service? Please visit our customer support page' | 'Are you sure you want to remove all items from your cart?' | 'Are you sure you want to remove this item from your cart?' | 'article' | 'articles' | 'As soon as possible' | 'Attention' | 'Billing address' | 'Billing and shipping address' | 'Billing and shipping information' | 'Cancel' | 'Cart' | 'Changing your address is currently not possible. Please contact customer support to change your address.' | 'Chosen filters' | 'City' | 'Clear filters' | 'Clear' | 'Click the button below to continue shopping.' | 'Close' | 'Company name' | 'Conceal value' | 'Continue shopping' | 'Continue' | 'Cost overview' | 'Country' | 'create account' | 'Create new list' | 'Delivery date' | 'Delivery expected in {0} {1}' | 'Double check your spelling' | 'Downloads' | 'Easily add your favorite products' | 'Edit billing address' | 'Edit shipping address' | 'Email' | 'Enter your email address and we will send you an email that will allow you to recover your password.' | 'Excl. VAT' | 'Explore by categories' | 'Exploring our products by category' | 'facet.categories' | 'facet.height' | 'facet.weight' | 'Features' | 'First name' | 'Forgot password?' | 'Fulfillment method' | 'General' | 'Hide filters' | 'Home' | 'If an account matches the email address you entered, instructions on how to recover the password will be sent to that email address shortly. If you do not receive this email, please contact Customer Support.' | 'Incl. VAT' | 'Includes' | 'Industry' | 'Information' | 'Language' | 'Last name' | 'List name already exists' | 'New list name' | 'New user?' | 'of' | 'Or continue as guest' | 'Order number' | 'Order' | 'Order confirmation' | 'Order date' | 'Order number' | 'Order' | 'Pay by invoice' | 'Pay' | 'Payment method' | 'Payment' | 'Password' | 'pc' | 'Phone' | 'Pick up' | 'Pickup address' | 'Please enter a valid email address' | 'Please enter a valid phone number' | 'Please Sign In' | 'PO Number' | 'Popular searches' | 'Postal Code' | 'Print' | 'Processing' | 'Product Features' | 'Product' | 'Products' | 'Quick access' | 'Recent searches' | 'Recently viewed' | 'Requested delivery date' | 'Remember me' | 'Remove all' | 'Requested delivery date' | 'Submitting…' | 'Submit email address' | 'Recover your password' | 'Reveal value' | 'Review and payment' | 'Save order' | 'Save' | 'Saved cart for later.' | 'Search' | 'Searching again using more general terms' | 'See all results' | 'Select a desired delivery date' | 'Select a list' | 'Selecting As Soon As Possible will enable us to send the products to you as they become available.' | 'Share your favorite list with others' | 'Ship' | 'Shipping address' | 'Shipping and handling' | 'Shipping details' | 'Shop more efficiently and quicker with a favorites list' | 'Show all' | 'Show filters' | 'Show less' | 'Show' | 'sign in' | 'Signing in…' | 'Sonic address' | 'Sonic Equipment' | 'Sorry, there are no products found' | 'Sorry, we could not find matches for' | 'Sort by' | 'sort.newest' | 'sort.price_asc' | 'sort.price_desc' | 'sort.relevance' | 'Specifications' | 'Submit' | 'Subtotal' | 'Suggestions' | 'tag.limited' | 'tag.new' | 'The expected delivery is an indication based on the product availability and the shipping location.' | 'The product has been added to your cart.' | 'The product has been removed from your cart.' | 'The product has been updated in your cart.' | 'There are no products in your shopping cart.' | 'Toggle navigation menu' | 'Total amount is' | 'Total' | 'Try another search' | 'Unable to add the product to your cart.' | 'Unable to empty your cart.' | 'Unable to remove the product from your cart.' | 'Unable to save cart for later.' | 'Unable to update the product in your cart.' | 'Updating address' | 'Use billing address' | 'Use fewer keywords' | 'Validating' | 'validation.badInput' | 'validation.customError' | 'validation.invalid' | 'validation.patternMismatch' | 'validation.rangeOverflow' | 'validation.rangeUnderflow' | 'validation.stepMismatch' | 'validation.tooLong' | 'validation.tooShort' | 'validation.typeMismatch' | 'validation.valid' | 'validation.valueMissing' | 'VAT Number' | 'VAT' | 'Welcome to Sonic Equipment. Please choose your country and language below.' | 'What are you searching for?' | 'You could try checking the spelling of your search query' | 'You could try exploring our products by category' | 'You could try' | 'You must ' | 'Your cart has been emptied.' | 'Your favorites are available on multiple devices' | 'Your shopping cart is still empty';
|
|
@@ -6,6 +6,7 @@ import { Button } from '../../buttons/button/button.js';
|
|
|
6
6
|
import { IconButton } from '../../buttons/icon-button/icon-button.js';
|
|
7
7
|
import { StrokeCloseboxIcon } from '../../icons/stroke/stroke-closebox-icon.js';
|
|
8
8
|
import { FormattedMessage } from '../../intl/formatted-message.js';
|
|
9
|
+
import { useFormattedMessage } from '../../intl/use-formatted-message.js';
|
|
9
10
|
import { Heading } from '../../typography/heading/heading.js';
|
|
10
11
|
import { Modal } from '../modal/modal.js';
|
|
11
12
|
import styles from './dialog.module.css.js';
|
|
@@ -14,6 +15,7 @@ function Footer({ cancelLabel, close, submitLabel }) {
|
|
|
14
15
|
return (jsxs(Fragment, { children: [jsx(Button, { withArrow: true, color: "primary", "data-test-selector": "dialogSubmit", size: "md", type: "submit", children: jsx(FormattedMessage, { id: submitLabel }) }), jsx(Button, { color: "secondary", "data-test-selector": "dialogCancel", onClick: close, size: "md", variant: "outline", children: jsx(FormattedMessage, { id: cancelLabel }) })] }));
|
|
15
16
|
}
|
|
16
17
|
function Dialog({ allowClose = true, cancelLabel = 'Close', children, className, footer = Footer, hasCloseButton = true, hideTitle, isDismissable, isFullScreen, isKeyboardDismissDisabled, isOpen, onOpenChange, onSubmit, shouldCloseOnInteractOutside, submitLabel = 'Submit', title, validationErrors, }) {
|
|
18
|
+
const t = useFormattedMessage();
|
|
17
19
|
return (jsx(Modal, { className: clsx(styles['modal-overlay'], typeof className === 'string' ? className : className?.modal), hasCloseButton: false, isDismissable: isDismissable, isFullScreen: isFullScreen, isKeyboardDismissDisabled: isKeyboardDismissDisabled, isOpen: isOpen, onOpenChange: onOpenChange, shouldCloseOnInteractOutside: shouldCloseOnInteractOutside, children: jsx(Dialog$1, { "aria-label": title, className: clsx(styles.dialog, typeof className === 'string' ? undefined : className?.dialog), "data-test-selector": "dialog", children: ({ close }) => (jsxs(Form, { className: clsx(styles.form, typeof className === 'string' ? undefined : className?.form), onSubmit: e => {
|
|
18
20
|
e.preventDefault();
|
|
19
21
|
e.stopPropagation();
|
|
@@ -22,7 +24,7 @@ function Dialog({ allowClose = true, cancelLabel = 'Close', children, className,
|
|
|
22
24
|
? undefined
|
|
23
25
|
: className?.heading), size: "xs", children: title })), hasCloseButton && (jsx("div", { className: clsx(styles.close, typeof className === 'string'
|
|
24
26
|
? undefined
|
|
25
|
-
: className?.close), children: jsx(IconButton, { color: "secondary", isDisabled: !allowClose, onClick: close, children: jsx(StrokeCloseboxIcon, {}) }) }))] }), jsx("div", { className: clsx(styles.content, typeof className === 'string' ? undefined : className?.content), children: children instanceof Function ? children({ close }) : children }), jsx("footer", { className: clsx(styles.footer, typeof className === 'string' ? undefined : className?.footer), children: footer instanceof Function
|
|
27
|
+
: className?.close), children: jsx(IconButton, { "aria-label": t('Close'), color: "secondary", isDisabled: !allowClose, onClick: close, children: jsx(StrokeCloseboxIcon, {}) }) }))] }), jsx("div", { className: clsx(styles.content, typeof className === 'string' ? undefined : className?.content), children: children instanceof Function ? children({ close }) : children }), jsx("footer", { className: clsx(styles.footer, typeof className === 'string' ? undefined : className?.footer), children: footer instanceof Function
|
|
26
28
|
? footer({ cancelLabel, close, submitLabel })
|
|
27
29
|
: footer })] })) }) }));
|
|
28
30
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { Button } from '../../buttons/button/button.js';
|
|
5
|
+
import { TextField } from '../../forms/text-field/text-field.js';
|
|
6
|
+
import { useFormattedMessage } from '../../intl/use-formatted-message.js';
|
|
7
|
+
import { Message } from '../../message/message.js';
|
|
8
|
+
import { useRecoverPassword } from '../../shared/api/storefront/hooks/authentication/use-recover-password.js';
|
|
9
|
+
import { validateEmail } from '../../shared/model/address.js';
|
|
10
|
+
import { Dialog } from '../dialog/dialog.js';
|
|
11
|
+
|
|
12
|
+
function RecoverPasswordDialog({ isOpen, onOpenChange, }) {
|
|
13
|
+
const t = useFormattedMessage();
|
|
14
|
+
const { error, isPending, isSuccess, mutate: recoverPassword, reset, } = useRecoverPassword();
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (isOpen === true)
|
|
17
|
+
reset();
|
|
18
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
19
|
+
}, [isOpen]);
|
|
20
|
+
const onSubmit = (event) => {
|
|
21
|
+
const form = event.currentTarget;
|
|
22
|
+
const formData = new FormData(form);
|
|
23
|
+
const email = formData.get('email')?.toString();
|
|
24
|
+
if (!email)
|
|
25
|
+
throw new Error('Email value is required');
|
|
26
|
+
recoverPassword({
|
|
27
|
+
userName: email,
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
return (jsxs(Dialog, { allowClose: false, footer: isSuccess ? (jsx(Button, { onClick: () => onOpenChange(false), children: t('Close') })) : (jsxs(Fragment, { children: [jsx(Button, { isLoading: isPending && t('Submitting…'), type: "submit", withArrow: true, children: t('Submit email address') }), jsx(Button, { color: "secondary", isDisabled: isPending, onClick: () => onOpenChange(false), variant: "outline", children: t('Cancel') })] })), hasCloseButton: false, isDismissable: false, isKeyboardDismissDisabled: true, isOpen: isOpen, onOpenChange: onOpenChange, onSubmit: event => onSubmit(event), shouldCloseOnInteractOutside: false, title: t('Recover your password'), children: [!isSuccess && (jsxs(Fragment, { children: [error && (jsx(Message, { type: "danger", children: t('An unexpected error occured. Please try again.') })), jsx("p", { children: t('Enter your email address and we will send you an email that will allow you to recover your password.') }), jsx(TextField, { "data-test-selector": "resetPassword_userName", isDisabled: isPending, isRequired: true, label: t('Email'), name: "email", showLabel: true, type: "email", validate: value => {
|
|
31
|
+
if (!value)
|
|
32
|
+
return value;
|
|
33
|
+
return (validateEmail(value) || t('Please enter a valid email address'));
|
|
34
|
+
} })] })), isSuccess && (jsx("p", { children: t('If an account matches the email address you entered, instructions on how to recover the password will be sent to that email address shortly. If you do not receive this email, please contact Customer Support.') }))] }));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { RecoverPasswordDialog };
|
|
@@ -7,16 +7,15 @@ import { List, ListItem } from '../../lists/ul/list.js';
|
|
|
7
7
|
import { useFavorite } from '../../shared/providers/favorite-provider.js';
|
|
8
8
|
import { RouteButton } from '../../shared/routing/route-button.js';
|
|
9
9
|
import { Dialog } from '../dialog/dialog.js';
|
|
10
|
-
import styles from './sign-in-dialog.module.css.js';
|
|
11
10
|
|
|
12
11
|
const getLocation = () => typeof window === 'undefined' ? undefined : window.location;
|
|
13
12
|
function SignInDialog({ isOpen, onOpenChange }) {
|
|
14
13
|
const { signInUrl } = useFavorite();
|
|
15
14
|
const t = useFormattedMessage();
|
|
16
15
|
const location = getLocation();
|
|
17
|
-
if (!location
|
|
16
|
+
if (!location)
|
|
18
17
|
return null;
|
|
19
|
-
return (jsx(Dialog, { isDismissable: true,
|
|
18
|
+
return (jsx(Dialog, { isDismissable: true, footer: jsxs(Fragment, { children: [jsx(RouteButton, { withArrow: true, color: "primary", href: `${signInUrl}?returnUrl=${encodeURIComponent(location.pathname + location.search)}`, onClick: () => onOpenChange(false), variant: "solid", children: jsx(FormattedMessage, { id: "sign in" }) }), jsx(RouteButton, { color: "secondary", href: `${signInUrl}?returnUrl=${encodeURIComponent(location.pathname + location.search)}`, onClick: () => onOpenChange(false), variant: "outline", children: jsx(FormattedMessage, { id: "create account" }) })] }), isOpen: isOpen, onOpenChange: onOpenChange, title: t('Shop more efficiently and quicker with a favorites list'), children: jsxs(List, { children: [jsx(ListItem, { icon: jsx(SolidOkayIcon, { fill: "var(--color-status-available)" }), text: t('Easily add your favorite products') }), jsx(ListItem, { icon: jsx(SolidOkayIcon, { fill: "var(--color-status-available)" }), text: t('Your favorites are available on multiple devices') }), jsx(ListItem, { icon: jsx(SolidOkayIcon, { fill: "var(--color-status-available)" }), text: t('Share your favorite list with others') })] }) }));
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
export { SignInDialog };
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { jsx } from 'react/jsx-runtime';
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useState, useMemo } from 'react';
|
|
3
|
+
import { RecoverPasswordDialog } from '../../../modals/recover-password/recover-password-dialog.js';
|
|
3
4
|
import { useCreateGuestAccount } from '../../../shared/api/storefront/hooks/authentication/use-create-guest-account.js';
|
|
4
5
|
import { useFetchSession } from '../../../shared/api/storefront/hooks/authentication/use-fetch-session.js';
|
|
5
6
|
import { useSignIn } from '../../../shared/api/storefront/hooks/authentication/use-sign-in.js';
|
|
6
7
|
import { isRequestError } from '../../../shared/fetch/request.js';
|
|
8
|
+
import { useDisclosure } from '../../../shared/hooks/use-disclosure.js';
|
|
7
9
|
import { useNavigate } from '../../../shared/routing/use-navigate.js';
|
|
8
10
|
import { SignInForm } from '../../../sign-in-form/sign-in-form.js';
|
|
9
11
|
import { Page } from '../../components/page/page.js';
|
|
@@ -20,6 +22,7 @@ const IMAGE = {
|
|
|
20
22
|
function SignInPage({ returnUrl } = {}) {
|
|
21
23
|
const { navigate } = useNavigate();
|
|
22
24
|
const [isSuccess, setIsSuccess] = useState(false);
|
|
25
|
+
const { isOpen: isRecoverPasswordDialogOpen, setIsOpen: setRecoverPasswordDialogOpen, } = useDisclosure(false);
|
|
23
26
|
const { data: session } = useFetchSession();
|
|
24
27
|
const { error: errorSignIn, isPending: isPendingSignIn, mutate: signIn, reset: resetSignIn, } = useSignIn();
|
|
25
28
|
const { error: errorCreateGuest, isPending: isPendingCreateGuest, mutate: createGuest, reset: resetCreateGuest, } = useCreateGuestAccount();
|
|
@@ -40,6 +43,7 @@ function SignInPage({ returnUrl } = {}) {
|
|
|
40
43
|
navigate(returnUrl || PATHS.ACCOUNT, { reload: true });
|
|
41
44
|
};
|
|
42
45
|
const allowGuestSignIn = returnUrl === PATHS.CHECKOUT_SHIPPING;
|
|
46
|
+
const createAccountPath = `${PATHS.ACCOUNT_CREATE}${returnUrl ? `?returnUrl=${returnUrl}` : ''}`;
|
|
43
47
|
const onSubmit = ({ data }) => {
|
|
44
48
|
resetSignIn();
|
|
45
49
|
resetCreateGuest();
|
|
@@ -56,7 +60,10 @@ function SignInPage({ returnUrl } = {}) {
|
|
|
56
60
|
}, { onSuccess });
|
|
57
61
|
}
|
|
58
62
|
};
|
|
59
|
-
|
|
63
|
+
const onRecoverPasswordDialogOpen = () => {
|
|
64
|
+
setRecoverPasswordDialogOpen(true);
|
|
65
|
+
};
|
|
66
|
+
return (jsxs(Fragment, { children: [jsx(Page, { fullHeight: true, 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) })] }));
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
export { SignInPage };
|
|
@@ -4,7 +4,6 @@ interface PaymentProps {
|
|
|
4
4
|
cart: CartModel;
|
|
5
5
|
form: string;
|
|
6
6
|
isProcessing: boolean;
|
|
7
|
-
isValidatingVAT: boolean;
|
|
8
7
|
onError?: (error: unknown, result: AdyenPaymentModel | null) => void;
|
|
9
8
|
onPaymentComplete: ({ cartId }: {
|
|
10
9
|
cartId: string;
|
|
@@ -12,5 +11,5 @@ interface PaymentProps {
|
|
|
12
11
|
onProcessing: (processing: boolean) => void;
|
|
13
12
|
onValidatingVAT: (validating: boolean) => void;
|
|
14
13
|
}
|
|
15
|
-
export declare function Payment({ atp, cart: _cart, form, isProcessing,
|
|
14
|
+
export declare function Payment({ atp, cart: _cart, form, isProcessing, onError: _onError, onPaymentComplete, onProcessing, onValidatingVAT, }: PaymentProps): import("react/jsx-runtime").JSX.Element;
|
|
16
15
|
export {};
|
|
@@ -18,7 +18,6 @@ import { usePlaceOrder } from '../../../../shared/api/storefront/hooks/cart/use-
|
|
|
18
18
|
import { useInvalidateAdyen } from '../../../../shared/api/storefront/hooks/payment/use-invalidate-adyen.js';
|
|
19
19
|
import { validateVATNumber } from '../../../../shared/api/storefront/services/finance-service.js';
|
|
20
20
|
import { useDataLayer } from '../../../../shared/ga/use-data-layer.js';
|
|
21
|
-
import { useDebouncedCallback } from '../../../../shared/hooks/use-debounced-callback.js';
|
|
22
21
|
import { currencySymbolToISO } from '../../../../shared/model/currency.js';
|
|
23
22
|
import { environment } from '../../../../shared/utils/environment.js';
|
|
24
23
|
import { BillingAndInvoiceInformation } from '../../components/billing-and-invoice-information.js';
|
|
@@ -26,7 +25,7 @@ import { useHasReturnedFromAdyen } from '../hooks/use-has-returned-from-adyen.js
|
|
|
26
25
|
import { AdyenPayment } from './adyen-payment.js';
|
|
27
26
|
import styles from './payment.module.css.js';
|
|
28
27
|
|
|
29
|
-
function Payment({ atp, cart: _cart, form, isProcessing,
|
|
28
|
+
function Payment({ atp, cart: _cart, form, isProcessing, onError: _onError, onPaymentComplete, onProcessing, onValidatingVAT, }) {
|
|
30
29
|
const { createEcommerceEvent, dataLayer } = useDataLayer();
|
|
31
30
|
const { mutate: patchCart } = usePatchCart();
|
|
32
31
|
const { mutate: placeOrder } = usePlaceOrder();
|
|
@@ -45,6 +44,7 @@ function Payment({ atp, cart: _cart, form, isProcessing, isValidatingVAT, onErro
|
|
|
45
44
|
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState(cart.paymentOptions?.paymentMethods?.[0]?.name || 'ADY');
|
|
46
45
|
const countryCode = _cart.billTo?.country?.abbreviation;
|
|
47
46
|
const currencyCode = currencySymbolToISO[cart.currencySymbol];
|
|
47
|
+
const lastVATNumber = useRef(cart.customerVatNumber);
|
|
48
48
|
if (!currencyCode)
|
|
49
49
|
throw new Error('Currency code not found for cart with currency symbol: ' +
|
|
50
50
|
cart.currencySymbol);
|
|
@@ -53,7 +53,7 @@ function Payment({ atp, cart: _cart, form, isProcessing, isValidatingVAT, onErro
|
|
|
53
53
|
cart.billTo?.id &&
|
|
54
54
|
countryCode;
|
|
55
55
|
const hasReturnedFromAdyen = useHasReturnedFromAdyen();
|
|
56
|
-
const isDisabled = hasReturnedFromAdyen || isProcessing
|
|
56
|
+
const isDisabled = hasReturnedFromAdyen || isProcessing;
|
|
57
57
|
useEffect(() => {
|
|
58
58
|
cartRef.current = _cart;
|
|
59
59
|
}, [_cart]);
|
|
@@ -68,41 +68,48 @@ function Payment({ atp, cart: _cart, form, isProcessing, isValidatingVAT, onErro
|
|
|
68
68
|
acc[method.name] = t(`paymentMethod.${method.description}`);
|
|
69
69
|
return acc;
|
|
70
70
|
}, {});
|
|
71
|
-
const
|
|
72
|
-
if (!value)
|
|
73
|
-
|
|
71
|
+
const validateVAT = useCallback(async (value) => {
|
|
72
|
+
if (!value) {
|
|
73
|
+
setValidationErrors({});
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
74
76
|
if (value.length < 8) {
|
|
75
|
-
|
|
77
|
+
setValidationErrors({
|
|
76
78
|
...validationErrors,
|
|
77
79
|
vatNumber: t('validation.tooShort'),
|
|
78
80
|
});
|
|
81
|
+
return false;
|
|
79
82
|
}
|
|
80
83
|
try {
|
|
84
|
+
if (lastVATNumber.current === value)
|
|
85
|
+
return true;
|
|
81
86
|
onValidatingVAT(true);
|
|
82
87
|
const response = await validateVATNumber({ vatNumber: value });
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
delete prev.vatNumber;
|
|
86
|
-
return prev;
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
88
|
+
lastVATNumber.current = value;
|
|
89
|
+
if (!response.isValid) {
|
|
90
90
|
setValidationErrors({
|
|
91
91
|
...validationErrors,
|
|
92
92
|
vatNumber: t('validation.invalid'),
|
|
93
93
|
});
|
|
94
|
+
return false;
|
|
94
95
|
}
|
|
96
|
+
setValidationErrors(prev => {
|
|
97
|
+
delete prev.vatNumber;
|
|
98
|
+
return prev;
|
|
99
|
+
});
|
|
100
|
+
return true;
|
|
95
101
|
}
|
|
96
102
|
catch {
|
|
97
103
|
setValidationErrors({
|
|
98
104
|
...validationErrors,
|
|
99
105
|
vatNumber: t('validation.invalid'),
|
|
100
106
|
});
|
|
107
|
+
return false;
|
|
101
108
|
}
|
|
102
109
|
finally {
|
|
103
110
|
onValidatingVAT(false);
|
|
104
111
|
}
|
|
105
|
-
},
|
|
112
|
+
}, [onValidatingVAT, t, validationErrors]);
|
|
106
113
|
const onPlaceOrderCompleted = useCallback(({ cart, payment_type }) => {
|
|
107
114
|
dataLayer.push(createEcommerceEvent({
|
|
108
115
|
cart,
|
|
@@ -141,6 +148,11 @@ function Payment({ atp, cart: _cart, form, isProcessing, isValidatingVAT, onErro
|
|
|
141
148
|
const formData = new FormData(e.currentTarget);
|
|
142
149
|
const cart = cartRef.current;
|
|
143
150
|
setValidationErrors(prev => prev); // NOTE: This makes sure the form is revalidated on submit
|
|
151
|
+
const vatNumber = formData.get('vatNumber')?.toString();
|
|
152
|
+
if (vatNumber &&
|
|
153
|
+
lastVATNumber.current !== vatNumber &&
|
|
154
|
+
!(await validateVAT(vatNumber)))
|
|
155
|
+
return;
|
|
144
156
|
if (Object.keys(validationErrors).length > 0)
|
|
145
157
|
return;
|
|
146
158
|
if (isAdyenPayment) {
|
|
@@ -268,7 +280,7 @@ function Payment({ atp, cart: _cart, form, isProcessing, isValidatingVAT, onErro
|
|
|
268
280
|
MA: 'Maritime',
|
|
269
281
|
OT: 'Other',
|
|
270
282
|
/* eslint-enable sort-keys-fix/sort-keys-fix */
|
|
271
|
-
}, variant: "solid" }), jsx(TextField, { showLabel: true, defaultValue: cart.customerVatNumber, isDisabled: isDisabled, label: t('VAT Number'), name: "vatNumber",
|
|
283
|
+
}, variant: "solid" }), jsx(TextField, { showLabel: true, defaultValue: cart.customerVatNumber, isDisabled: isDisabled, label: t('VAT Number'), name: "vatNumber", onBlur: e => validateVAT(e.target.value), validate: () => validationErrors.vatNumber ?? true }), 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,
|
|
272
284
|
// TODO: Use the correct Adyen environment before going live
|
|
273
285
|
environment: environment === 'production' ? 'live' : 'test', isDisabled: isDisabled, onComplete: onComplete, onError: onError, orderAmount: cart.orderGrandTotal, returnUrl:
|
|
274
286
|
/* eslint-disable ssr-friendly/no-dom-globals-in-react-fc */
|
|
@@ -24,7 +24,7 @@ function PaymentPageContent({ atp, cart, formId, hasAtp, isProcessing, isValidat
|
|
|
24
24
|
], title: t('Review and payment'), children: jsxs(CheckoutPageLayout, { actions: {
|
|
25
25
|
primary: (jsx(Button, { withArrow: true, "data-test-selector": "checkoutReviewAndSubmit_placeOrder", form: formId, isDisabled: isProcessing, isLoading: isProcessing ? (jsx(FormattedMessage, { id: "Processing" })) : isValidatingVAT ? (jsx(FormattedMessage, { id: "Validating" })) : (false), type: "submit", children: jsx(FormattedMessage, { id: "Pay" }) })),
|
|
26
26
|
}, mobileSummary: jsx(CartTotalsSummary, { totalAmount: cart.orderGrandTotalDisplay }), overview: jsx(CartTotals, { deliveryDate: hasAtp ? undefined : cart.requestedDeliveryDate, fulfillmentMethod: cart.fulfillmentMethod, isPayByInvoice: (cart.paymentOptions?.paymentMethods?.length || 1) <= 1 &&
|
|
27
|
-
cart.paymentMethod?.name === 'PBI', shippingCost: cart.shippingAndHandlingDisplay, subtotal: cart.orderSubTotalDisplay, tax: cart.totalTaxDisplay, total: cart.orderGrandTotalDisplay, vatPercentage: cart.cartLines?.[0]?.pricing?.vatRate || 0 }), children: [jsx(CheckoutPageSection, { hasBorder: false, title: jsx(FormattedMessage, { id: "Payment" }), children: jsx(CheckoutPageSectionContent, { children: jsx(Payment, { atp: atp, cart: cart, form: formId, isProcessing: isProcessing,
|
|
27
|
+
cart.paymentMethod?.name === 'PBI', shippingCost: cart.shippingAndHandlingDisplay, subtotal: cart.orderSubTotalDisplay, tax: cart.totalTaxDisplay, total: cart.orderGrandTotalDisplay, vatPercentage: cart.cartLines?.[0]?.pricing?.vatRate || 0 }), children: [jsx(CheckoutPageSection, { hasBorder: false, title: jsx(FormattedMessage, { id: "Payment" }), children: jsx(CheckoutPageSectionContent, { children: jsx(Payment, { atp: atp, cart: cart, form: formId, isProcessing: isProcessing, onPaymentComplete: onPaymentComplete, onProcessing: setIsProcessing, onValidatingVAT: setIsValidatingVAT }) }) }), jsx(CheckoutPageSection, { hasBorder: false, title: jsx(FormattedMessage, { id: "Order" }), children: jsx(CheckoutPageSectionContent, { stretch: true, children: jsx(OrderLineList, { children: cart.cartLines?.map(cartLine => (jsx(OrderLineCard, { deliveryDate: cartLine.atp?.date, href: cartLine.productUri, image: {
|
|
28
28
|
fit: 'contain',
|
|
29
29
|
image: {
|
|
30
30
|
'1': cartLine.smallImagePath,
|
|
@@ -53,7 +53,7 @@ function EditAddressesForm({ billTo, countries, isLoading, isPickup, onSubmit, }
|
|
|
53
53
|
if (!value)
|
|
54
54
|
return value;
|
|
55
55
|
return (validateEmail(value) ||
|
|
56
|
-
t('Please enter a valid
|
|
56
|
+
t('Please enter a valid email address'));
|
|
57
57
|
} }) }), jsx("div", { className: styles['span-2'], children: jsx(TextField, { defaultValue: cart?.notes, isDisabled: isLoading, isMultiline: true, label: t('Add order notes'), name: "notes", rows: 3, showLabel: true }) })] }) }) }), jsx(CheckoutPageSection, { title: jsx(FormattedMessage, { id: isPickup ? 'Pickup address' : 'Shipping address' }), children: jsx(CheckoutPageSectionContent, { children: jsx(Fragment, { children: isPickup ? (jsx(SonicAddress, {})) : (jsxs("div", { className: styles['use-invoice-checkbox'], children: [jsx(Checkbox, { "data-test-selector": "checkboxUseBillingAddress", isDisabled: true, isSelected: true, children: jsx(FormattedMessage, { id: "Use billing address" }) }), jsx(InfoIconTooltip, { variant: "stroke", children: t('Changing your address is currently not possible. Please contact customer support to change your address.') })] })) }) }) })] }));
|
|
58
58
|
}
|
|
59
59
|
|
package/dist/pages/paths.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare const PATHS: {
|
|
2
2
|
readonly ACCOUNT: "/MyAccount";
|
|
3
3
|
readonly ACCOUNT_ADDRESSES: "/MyAccount/Addresses";
|
|
4
|
+
readonly ACCOUNT_CREATE: "/MyAccount/CreateAccount";
|
|
4
5
|
readonly CART: "/cart";
|
|
5
6
|
readonly CHECKOUT_SHIPPING: "/CheckoutShipping";
|
|
6
7
|
readonly CHECKOUT_SHIPPING_VIA_SIGNIN: "/signin?returnUrl=/CheckoutShipping";
|
package/dist/pages/paths.js
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useQueryClient, useMutation } from '@tanstack/react-query';
|
|
2
|
+
import { recoverPassword } from '../../services/authentication-service.js';
|
|
3
|
+
|
|
4
|
+
function useRecoverPassword() {
|
|
5
|
+
const queryClient = useQueryClient();
|
|
6
|
+
return useMutation({
|
|
7
|
+
mutationFn: async ({ userName }) => {
|
|
8
|
+
const session = await recoverPassword({ userName });
|
|
9
|
+
queryClient.setQueryData(['session'], session);
|
|
10
|
+
return session;
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { useRecoverPassword };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { AccountModel, PatchSessionModel, SessionModel
|
|
2
|
-
export declare function fetchSession(): Promise<
|
|
1
|
+
import { AccountModel, PatchSessionModel, SessionModel } from '../model/storefront.model';
|
|
2
|
+
export declare function fetchSession(): Promise<SessionModel>;
|
|
3
3
|
export declare function patchSession({ session, }: {
|
|
4
4
|
session: PatchSessionModel;
|
|
5
|
-
}): Promise<
|
|
5
|
+
}): Promise<SessionModel>;
|
|
6
6
|
export interface AuthenticationResponse {
|
|
7
7
|
access_token: string;
|
|
8
8
|
error_description: string;
|
|
@@ -28,3 +28,6 @@ export declare function signOut(): Promise<void>;
|
|
|
28
28
|
export declare function createGuestAccount({ defaultWarehouseId, }: {
|
|
29
29
|
defaultWarehouseId: string;
|
|
30
30
|
}): Promise<AccountModel>;
|
|
31
|
+
export declare function recoverPassword({ userName, }: {
|
|
32
|
+
userName: string;
|
|
33
|
+
}): Promise<SessionModel>;
|
|
@@ -78,5 +78,19 @@ async function createGuestAccount({ defaultWarehouseId, }) {
|
|
|
78
78
|
});
|
|
79
79
|
return body;
|
|
80
80
|
}
|
|
81
|
+
async function recoverPassword({ userName, }) {
|
|
82
|
+
const { body } = await request({
|
|
83
|
+
body: {
|
|
84
|
+
resetPassword: true,
|
|
85
|
+
userName,
|
|
86
|
+
},
|
|
87
|
+
headers: {
|
|
88
|
+
'Content-Type': 'application/json',
|
|
89
|
+
},
|
|
90
|
+
method: 'PATCH',
|
|
91
|
+
url: `${config.SHOP_API_URL}/api/v1/sessions/current`,
|
|
92
|
+
});
|
|
93
|
+
return body;
|
|
94
|
+
}
|
|
81
95
|
|
|
82
|
-
export { createGuestAccount, createSession, fetchSession, patchSession, signIn, signOut };
|
|
96
|
+
export { createGuestAccount, createSession, fetchSession, patchSession, recoverPassword, signIn, signOut };
|
|
@@ -3,6 +3,20 @@ interface RequestErrorOptions {
|
|
|
3
3
|
context?: string;
|
|
4
4
|
options?: RequestOptions;
|
|
5
5
|
}
|
|
6
|
+
interface RequestErrorToJSONReturnType {
|
|
7
|
+
context: string | undefined;
|
|
8
|
+
error: {
|
|
9
|
+
message: string;
|
|
10
|
+
stack: string | undefined;
|
|
11
|
+
};
|
|
12
|
+
isRequestError: boolean;
|
|
13
|
+
request: RequestOptions | object;
|
|
14
|
+
response: {
|
|
15
|
+
body: unknown;
|
|
16
|
+
status: number;
|
|
17
|
+
statusText: string | undefined;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
6
20
|
export declare class RequestError extends Error {
|
|
7
21
|
isRequestError: boolean;
|
|
8
22
|
status: number;
|
|
@@ -11,20 +25,7 @@ export declare class RequestError extends Error {
|
|
|
11
25
|
context?: string;
|
|
12
26
|
options?: RequestOptions;
|
|
13
27
|
constructor(responseOrError: Response | any, options?: RequestErrorOptions, status?: number, statusText?: string);
|
|
14
|
-
toJSON():
|
|
15
|
-
context: string | undefined;
|
|
16
|
-
error: {
|
|
17
|
-
message: string;
|
|
18
|
-
stack: string | undefined;
|
|
19
|
-
};
|
|
20
|
-
isRequestError: boolean;
|
|
21
|
-
request: {};
|
|
22
|
-
response: {
|
|
23
|
-
body: any;
|
|
24
|
-
status: number;
|
|
25
|
-
statusText: string | undefined;
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
+
toJSON(): RequestErrorToJSONReturnType;
|
|
28
29
|
}
|
|
29
30
|
export declare const isRequestError: (error: any) => error is RequestError;
|
|
30
31
|
export declare class BadRequestError extends RequestError {
|
|
@@ -11,15 +11,17 @@ export type SubmitData = LoginData | GuestLoginData;
|
|
|
11
11
|
export type ErrorMessage = 'Access denied' | 'Unexpected error';
|
|
12
12
|
export interface SignInFormProps {
|
|
13
13
|
allowGuestSignIn?: boolean;
|
|
14
|
+
createAccountPath: string;
|
|
14
15
|
errorMessage?: ErrorMessage;
|
|
15
16
|
initialEmail?: string;
|
|
16
17
|
initialRememberMe?: boolean;
|
|
17
18
|
isDisabled?: boolean;
|
|
18
19
|
isPendingGuestSignIn?: boolean;
|
|
19
20
|
isPendingUserSignIn?: boolean;
|
|
21
|
+
onRecoverPasswordDialogOpen?: () => void;
|
|
20
22
|
onSubmit?: ({ data }: {
|
|
21
23
|
data: SubmitData;
|
|
22
24
|
}) => void;
|
|
23
25
|
}
|
|
24
|
-
export declare function SignInForm({ allowGuestSignIn, errorMessage, initialEmail, initialRememberMe, isDisabled: _isDisabled, isPendingGuestSignIn, isPendingUserSignIn, onSubmit, }: SignInFormProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export declare function SignInForm({ allowGuestSignIn, createAccountPath, errorMessage, initialEmail, initialRememberMe, isDisabled: _isDisabled, isPendingGuestSignIn, isPendingUserSignIn, onRecoverPasswordDialogOpen, onSubmit, }: SignInFormProps): import("react/jsx-runtime").JSX.Element;
|
|
25
27
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { Form } from 'react-aria-components';
|
|
4
4
|
import clsx from 'clsx';
|
|
5
5
|
import { Button } from '../buttons/button/button.js';
|
|
@@ -9,11 +9,12 @@ import { TextField } from '../forms/text-field/text-field.js';
|
|
|
9
9
|
import { useFormattedMessage } from '../intl/use-formatted-message.js';
|
|
10
10
|
import { Message } from '../message/message.js';
|
|
11
11
|
import { validateEmail } from '../shared/model/address.js';
|
|
12
|
+
import { RouteLink } from '../shared/routing/route-link.js';
|
|
12
13
|
import { Heading } from '../typography/heading/heading.js';
|
|
13
14
|
import styles from './sign-in-form.module.css.js';
|
|
14
15
|
|
|
15
16
|
const GUEST_SIGN_IN_BUTTON_NAME = 'guestSignin';
|
|
16
|
-
function SignInForm({ allowGuestSignIn = false, errorMessage, initialEmail, initialRememberMe, isDisabled: _isDisabled = false, isPendingGuestSignIn = false, isPendingUserSignIn = false, onSubmit = () => { }, }) {
|
|
17
|
+
function SignInForm({ allowGuestSignIn = false, createAccountPath, errorMessage, initialEmail, initialRememberMe, isDisabled: _isDisabled = false, isPendingGuestSignIn = false, isPendingUserSignIn = false, onRecoverPasswordDialogOpen = () => { }, onSubmit = () => { }, }) {
|
|
17
18
|
const t = useFormattedMessage();
|
|
18
19
|
const title = t('sign in');
|
|
19
20
|
const isDisabled = isPendingUserSignIn || isPendingGuestSignIn || _isDisabled;
|
|
@@ -50,8 +51,8 @@ function SignInForm({ allowGuestSignIn = false, errorMessage, initialEmail, init
|
|
|
50
51
|
return (jsxs(Form, { "aria-label": title, autoComplete: "off", className: clsx(styles['sign-in-form'], styles['form']), onSubmit: handleSubmit, validationBehavior: "native", children: [jsx("header", { className: styles['form-header'], children: jsx(Heading, { "data-test-selector": "PageTitle", italic: true, size: "m", tag: "h1", uppercase: true, children: title }) }), errorMessage === 'Access denied' && (jsx(Message, { type: "danger", children: t('Your email and password were not recognized.') })), errorMessage === 'Unexpected error' && (jsx(Message, { type: "danger", children: t('An unexpected error occured. Please try again.') })), jsxs("fieldset", { className: clsx(styles['form-fieldset'], styles['columns']), children: [jsx("div", { className: styles['form-segment'], children: jsx(TextField, { "data-test-selector": "signIn_userName", defaultValue: initialEmail, isDisabled: isDisabled, isRequired: true, label: t('Email'), name: "email", showLabel: true, type: "email", validate: value => {
|
|
51
52
|
if (!value)
|
|
52
53
|
return value;
|
|
53
|
-
return (validateEmail(value) || t('Please enter a valid
|
|
54
|
-
} }) }), jsx("div", { className: styles['form-segment'], children: jsx(TextField, { "data-test-selector": "signIn_password", isDisabled: isDisabled, isRequired: true, label: t('Password'), name: "password", showLabel: true, type: "password" }) })] }), jsxs("footer", { className: styles['form-footer'], children: [jsx("div", { className: styles['form-segment'], children: jsx(Switch, { className: styles['floating'], defaultSelected: initialRememberMe, isDisabled: isDisabled, name: "rememberMe", value: "true", children: t('Remember me') }) }), jsx("div", { className: styles['form-segment'], children: jsx(Button, { "data-test-selector": "signIn_submit", isDisabled: isDisabled, isLoading: isPendingUserSignIn && t('Signing in…'), type: "submit", withArrow: true, children: t('sign in') }) }), jsxs("div", { className: styles['form-segment'], children: [jsx("p", { children: jsx(Link, { color: "primary", "data-test-selector": "signIn_forgotPassword", hasUnderline: true,
|
|
54
|
+
return (validateEmail(value) || t('Please enter a valid email address'));
|
|
55
|
+
} }) }), jsx("div", { className: styles['form-segment'], children: jsx(TextField, { "data-test-selector": "signIn_password", isDisabled: isDisabled, isRequired: true, label: t('Password'), name: "password", showLabel: true, type: "password" }) })] }), jsxs("footer", { className: styles['form-footer'], children: [jsx("div", { className: styles['form-segment'], children: jsx(Switch, { className: styles['floating'], defaultSelected: initialRememberMe, isDisabled: isDisabled, name: "rememberMe", value: "true", children: t('Remember me') }) }), jsx("div", { className: styles['form-segment'], children: jsx(Button, { "data-test-selector": "signIn_submit", isDisabled: isDisabled, isLoading: isPendingUserSignIn && t('Signing in…'), type: "submit", withArrow: true, children: t('sign in') }) }), jsxs("div", { className: styles['form-segment'], children: [jsx("p", { children: jsx(Link, { color: "primary", "data-test-selector": "signIn_forgotPassword", hasUnderline: true, isDisabled: isDisabled, onClick: onRecoverPasswordDialogOpen, children: t('Forgot password?') }) }), jsx("p", { 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("div", { className: styles['form-segment'], 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') }) }))] })] }));
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
export { SignInForm };
|
package/dist/styles.css
CHANGED
|
@@ -828,7 +828,7 @@
|
|
|
828
828
|
|
|
829
829
|
.button-module-V4meK.button-module-U5IxM .button-module-13ndF {
|
|
830
830
|
position: absolute;
|
|
831
|
-
right: var(--space-
|
|
831
|
+
right: var(--space-12);
|
|
832
832
|
}
|
|
833
833
|
|
|
834
834
|
.button-module-V4meK.button-module-U5IxM .button-module-ydQAo {
|
|
@@ -1799,6 +1799,7 @@
|
|
|
1799
1799
|
margin: 0;
|
|
1800
1800
|
color: var(--color-brand-black);
|
|
1801
1801
|
font-weight: var(--font-weight-normal);
|
|
1802
|
+
text-wrap: pretty;
|
|
1802
1803
|
transition: all 0.2s ease-out;
|
|
1803
1804
|
}
|
|
1804
1805
|
|
|
@@ -2241,6 +2242,7 @@
|
|
|
2241
2242
|
|
|
2242
2243
|
.dialog-module-qKzgy {
|
|
2243
2244
|
position: relative;
|
|
2245
|
+
max-width: 403px;
|
|
2244
2246
|
padding: var(--space-24);
|
|
2245
2247
|
padding-right: var(--space-32);
|
|
2246
2248
|
}
|
|
@@ -2277,10 +2279,6 @@
|
|
|
2277
2279
|
gap: 8px;
|
|
2278
2280
|
}
|
|
2279
2281
|
|
|
2280
|
-
.sign-in-dialog-module-P-AHV {
|
|
2281
|
-
max-width: 403px;
|
|
2282
|
-
}
|
|
2283
|
-
|
|
2284
2282
|
.favorite-button-module-tXSS3:where(.favorite-button-module-l557q) {
|
|
2285
2283
|
animation: favorite-button-module-6Tsmy 0.4s ease-in-out 0s 1 normal both;
|
|
2286
2284
|
color: var(--color-brand-red);
|
|
@@ -5361,6 +5359,7 @@ button.swiper-pagination-bullet {
|
|
|
5361
5359
|
|
|
5362
5360
|
.message-module-Gi4uR {
|
|
5363
5361
|
grid-area: body;
|
|
5362
|
+
text-wrap: pretty;
|
|
5364
5363
|
}
|
|
5365
5364
|
|
|
5366
5365
|
/* variants */
|
|
@@ -6068,6 +6067,7 @@ button.swiper-pagination-bullet {
|
|
|
6068
6067
|
}
|
|
6069
6068
|
|
|
6070
6069
|
@media (width >= 768px) {.edit-address-form-module-s19Fg {
|
|
6070
|
+
align-items: flex-start;
|
|
6071
6071
|
grid-template-columns: 1fr 1fr
|
|
6072
6072
|
}
|
|
6073
6073
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sonic-equipment/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "141.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -140,8 +140,8 @@
|
|
|
140
140
|
"instantsearch.js": "4.77.3",
|
|
141
141
|
"js-cookie": "3.0.5",
|
|
142
142
|
"query-string": "9.1.1",
|
|
143
|
-
"react-aria": "3.
|
|
144
|
-
"react-aria-components": "1.
|
|
143
|
+
"react-aria": "3.38.1",
|
|
144
|
+
"react-aria-components": "1.7.1",
|
|
145
145
|
"react-instantsearch": "7.15.3",
|
|
146
146
|
"react-toastify": "10.0.6",
|
|
147
147
|
"react-transition-group": "4.4.5",
|