{"version":3,"sources":["webpack:///./ClientApp/services/Localizer.tsx","webpack:///./ClientApp/shared/assets/types.ts","webpack:///./ClientApp/shared/polyfills/customEvent.js","webpack:///./ClientApp/shared/components/svg/LeftChevronSVG.tsx","webpack:///./ClientApp/shared/components/svg/DownChevronSVG.tsx","webpack:///./ClientApp/shared/components/svg/UpChevronSVG.tsx","webpack:///./ClientApp/shared/hooks/useInViewOnce.ts","webpack:///./ClientApp/shared/assets/LazyImage.tsx","webpack:///./ClientApp/util/StringUtil.ts","webpack:///./ClientApp/services/ServerData.ts","webpack:///./ClientApp/features/commerce/pricing/pricing-result.ts","webpack:///./ClientApp/shared/components/svg/QuestionMarkCircleSVG.tsx","webpack:///./ClientApp/shared/components/svg/RightChevronSVG.tsx","webpack:///./ClientApp/shared/components/Spinner.tsx","webpack:///./ClientApp/features/commerce/product-details/data.ts","webpack:///./ClientApp/shared/breakpoints.ts","webpack:///./ClientApp/services/AxiosHelper.ts","webpack:///./ClientApp/shared/components/svg/PlayMediaSVG.tsx","webpack:///./ClientApp/shared/components/svg/FilePDFSVG.tsx","webpack:///./ClientApp/shared/components/svg/FileWithTextSVG.tsx","webpack:///./ClientApp/shared/components/svg/FileWordDocSVG.tsx","webpack:///./ClientApp/shared/components/svg/FileExcelSVG.tsx","webpack:///./ClientApp/shared/assets/AssetIcon.tsx","webpack:///./ClientApp/shared/components/svg/DownloadSVG.tsx","webpack:///./ClientApp/shared/assets/AssetDownloadIcon.tsx","webpack:///./ClientApp/shared/assets/ExternalMediaIFrame.tsx","webpack:///./ClientApp/shared/components/svg/HclZoomSVG.tsx","webpack:///./ClientApp/features/commerce/product-details/ProductImageModal.tsx","webpack:///./ClientApp/features/commerce/product-details/ProductAssets.tsx","webpack:///./ClientApp/shared/hooks/useSmoothScrollingPolyfill.tsx","webpack:///./ClientApp/shared/components/svg/FileSVG.tsx","webpack:///./ClientApp/features/commerce/recently-viewed/RecentlyViewedProduct.tsx","webpack:///./ClientApp/features/commerce/recently-viewed/useRecentlyViewedProducts.ts","webpack:///./ClientApp/features/commerce/recently-viewed/RecentlyViewedProducts.tsx","webpack:///./ClientApp/shared/edit/InlineAlert.tsx","webpack:///./ClientApp/shared/hooks/useRocEventHandler.tsx","webpack:///./ClientApp/shared/components/svg/XSVG.tsx","webpack:///./ClientApp/services/UserData.ts","webpack:///./ClientApp/shared/util/ErrorBoundary.tsx","webpack:///./ClientApp/shared/util/AccountUrls.ts","webpack:///./ClientApp/shared/polyfills/getAttributeNames.js","webpack:///./ClientApp/shared/util/ApplicationInit.tsx","webpack:///./ClientApp/shared/components/commerce/ProductPricing.tsx","webpack:///./ClientApp/shared/hooks/useDelayedDisplay.tsx","webpack:///./ClientApp/shared/util/events/rocEventUtils.ts","webpack:///../Roc.SharedClientApp/types/commerce/display-price.ts","webpack:///./ClientApp/features/commerce/product-details/types.ts","webpack:///./ClientApp/features/commerce/recently-viewed/index.tsx"],"names":["LocalizerResources","isAlreadyLogged","initializeLocalizerResources","windowWithResources","window","__ROC_LOCALIZER_RESOURCES__","Array","isArray","forEach","obj","Object","keys","key","Localizer","replaceStrings","length","console","warn","resource","replace","match","index","LocalizedSpan","props","resourceKey","args","localizedString","data-testid","toKebabCase","dangerouslySetInnerHTML","__html","AssetType","CustomEvent","event","params","bubbles","cancelable","detail","undefined","evt","document","createEvent","initCustomEvent","prototype","Event","LeftChevronSVG","className","rest","viewBox","classnames","focusable","aria-hidden","d","DownChevronSVG","UpChevronSVG","useInViewOnce","makeIterator","options","inViewHookResponse","useInView","useState","viewed","setViewed","useEffect","inView","newResponse","Symbol","iterator","LazyImage","placeholderImg","src","alt","backgroundImg","delay","inactiveClassName","loadedClassName","threshold","ref","loadedClassDisplayed","setLoadedClassDisplayed","setTimeout","imageSrc","classes","style","backgroundImage","value","trim","toLocaleLowerCase","toCamelCase","word","toLowerCase","toUpperCase","stripHtml","html","tmp","createElement","innerHTML","textContent","innerText","addEllipsis","text","cutoff","substring","trimString","ServerData","windowWithServerData","__ROC_SERVER_DATA__","initializeServerData","ParseQuantityRange","quantityRange","s","split","parseInt","Infinity","GetPriceForQuantity","prices","quantity","Error","min","max","GetPricingLine","pricingResult","productId","pricingLineId","line","products","pricingLines","id","pricingLine","isDefault","QuestionMarkCircleSVG","stroke","strokeWidth","fill","fillRule","transform","RightChevronSVG","Spinner","light","waitMs","align","block","parentCentered","useDelayedDisplay","spinner","partialDataCache","partialProductsDataCache","getPartialProductData","cacheKey","missingKeys","sort","join","additionalParams","name","paramValue","Promise","resolve","AxiosHelper","get","response","data","getBulkPartialProductsData","productIds","post","lg","md","sm","axios","create","defaults","headers","common","interceptors","request","use","config","method","getCsrfToken","csrfToken","RequestVerificationToken","addUnauthorizedAccessInterceptor","history","error","status","location","AccountUrls","Login","encodeURIComponent","pathname","reject","PlayMediaSVG","FilePDFSVG","FileWithTextSVG","FileWordDocSVG","FileExcelSVG","AssetIcon","asset","assetIconClasses","assetType","Media","YouTube","color","Document","assetUrl","pop","FileSVG","DownloadSVG","AssetDownloadIcon","downloadIconClasses","ExternalMediaIFrame","embeddedUrl","title","frameBorder","allow","allowFullScreen","HclZoomSVG","LazyDialog","React","lazy","then","module","default","Dialog","ProductImageModal","isOpen","closeModal","children","Suspense","fallback","onDismiss","aria-label","as","onClick","type","XSVG","LazyMediaQuery","productAssetPlaceholder","altText","sortOrder","thumbnailUrl","mainImageUrl","enlargedImageUrl","Image","compareAssets","a","b","ProductAssets","mainAsset","setMainAsset","altAssets","setAltAssets","isImageSliderModalOpen","setIsImageSliderModalOpen","carousel","useRef","nodes","Map","scrollCarousel","up","from","values","current","item","filter","node","innerWidth","scrollBy","clientHeight","smoothscroll","polyfill","assets","newMainAsset","find","assetsWithoutDocs","initAltAssets","it","assetScroller","maxWidth","minWidth","map","altAsset","i","c","set","preventDefault","href","download","mainImageContent","saleDiscount","isNewProduct","RecentlyViewedProduct","product","defaultAsset","mainImage","customUrl","summary","meta","url","displayPricing","displayPrice","dispatchRocEvent","productName","productGroupId","useTinySliderLazyLoading","data-src","ProductPricing","sparseProductDetailsStateKeys","repository","context","stateStr","localStorage","getItem","this","keyName","JSON","parse","timestamp","Date","valueOf","removeItem","filterOutInvalid","getAllMayIncludeInvalid","validProducts","setAll","now","timeSpan","MinimumTimeBeforeRecentlyViewedProductsAreValidated","outsideTimeSpan","p","lastViewed","withinTimeSpan","validProductIds","validOutsideTimeSpan","includes","concat","message","setItem","stringify","RecentlyViewedProducts","recentlyViewedProducts","currentProduct","active","setRecentlyViewedProducts","getProductStateAsync","productState","recentlyViewedProduct","getAllValidOnly","recentlyViewedProductDtos","currentProductDto","currentProductIndex","findIndex","splice","groupId","unshift","groupIndex","groupProducts","currentGroupDto","slice","useRecentlyViewedProducts","getInitialProductDetailsState","currentInstanceId","uuid","sliderRef","displaytitle","tnsPromise","tns","oneItemClass","twoItemClass","threeItemClass","fiveItemClass","executeResizeDebounced","debounce","initializeSlider","destroy","currentSliderDiv","getElementById","getTinySliderInstance","container","items","itemsToShow","slideBy","controls","mouseDrag","gutter","lazyload","loop","edgePadding","trackElement","parentElement","parentRocProductList","closest","containerWidth","offsetWidth","classList","remove","add","handleResize","addEventListener","removeEventListener","InlineAlert","defaultProps","theme","isTitleOnOwnRow","isCollapsed","onCollapse","axiosError","isAxiosError","eventType","dispatchEvent","useRocEventHandler","eventHandler","deps","removeHandlerCallback","addRocEventHandler","userDataPromise","getUserData","getUserDataNoCache","userData","requestToken","ErrorBoundary","lastRefreshedTimestamp","getTime","toString","reload","setState","state","renderError","Component","renderProps","BaseUrl","ACCOUNT_URL_BASE","ACCOUNT_LOGIN_URL","Login2fa","ACCOUNT_LOGIN_WITH_2FA_URL","Register","ACCOUNT_REGISTER_URL","ForgotPassword","ACCOUNT_FORGOT_PASSWORD_URL","LoginWithRecoveryCode","ACCOUNT_LOGIN_WITH_RECOVERY_CODE_URL","Logout","ACCOUNT_LOGOUT_URL","OrganizationReview","ACCOUNT_ORGANIZATION_REVIEW_URL","OrganizationSelector","ACCOUNT_ORGANIZATION_SELECTOR_URL","ExternalLogout","Element","getAttributeNames","attributes","result","getDataPropsFromAttributes","element","startsWith","attributeName","getAttribute","initReactApp","getReactElement","elementId","ReactDOM","render","initReactAppByAttribute","attribute","elements","querySelectorAll","inline","testId","roundValue","renderResult","css","priceClass","base","normalPrice","crossedOutPrice","highlightedPrice","GetCssClasses","displayRule","PriceDisplayRule","Missing","Normal","price","basePriceClass","salePrice","VariantPriceComponent","basePrice","renderNormalPrice","AmbiguousRange","roundedPrice","rawValue","toFixed","FormatAmbiguousPriceSpan","unitName","unit","label","renderAmbiguousPrice","FormatPrice","FormatPriceSpan","priceRange","to","display","setDisplay","timeout","clearTimeout","handler","__ROC_COMMERCE_PRODUCT_DETAILS__","InputAttributeType","ProductType","AddToCartExceptionCode"],"mappings":"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","file":"recently-viewed-products.f481e3d5.js","sourcesContent":["import { AddressResourceKeys } from 'features/address-book/resources';\r\nimport { ContactFormResourceKeys } from 'features/contact-form/resources';\r\nimport { CreatePasswordResourceKeys } from 'features/create-password/resources';\r\nimport { LoginWith2faResourceKeys } from 'features/login-with-2fa/resources';\r\nimport { LoginWithRecoveryCodeResourceKeys } from 'features/login-with-recovery-code/resources';\r\nimport { LoginResourceKeys } from 'features/login/resources';\r\nimport { MyAccountResourceKeys } from 'features/my-account/resources';\r\nimport { ResetPasswordResourceKeys } from 'features/reset-password/resources';\r\nimport { SubmitQuestionResourceKeys } from 'features/submit-question/resources';\r\nimport * as React from 'react';\r\nimport { toKebabCase } from 'util/StringUtil';\r\n\r\n// #region Commerce\r\nimport { CommerceResourceKeys } from 'features/commerce/CommerceResourceKeys';\r\n// #endregion\r\n\r\n// #region HCL\r\nimport { QuickBagsResourceKeys } from 'features/hcl/quick-bags/resources';\r\nimport { QuickLabelsResourceKeys } from 'features/hcl/quick-labels/resources';\r\nimport { ProductCatalogsResourceKeys } from 'features/hcl/product-catalogs/resources';\r\nimport { FileUploadResourceKeys } from 'features/hcl/file-upload/resources';\r\nimport { NewsletterKeys } from 'features/hcl/newsletter-signup/resources';\r\n// #endregion\r\n\r\n/**\r\n * Type that will hold all the global localization keys\r\n */\r\ntype LocalizerGlobalResourceKeys =\r\n\t| 'Success'\r\n\t| 'OfTotalPages'\r\n\t| 'Cancel'\r\n\t| 'Confirm'\r\n\t| 'FirstName'\r\n\t| 'LastName'\r\n\t| 'Email'\r\n\t| 'Username'\r\n\t| 'Amount'\r\n\t| 'NameOnCard'\r\n\t| 'CardNumber'\r\n\t| 'ExpDate'\r\n\t| 'ExpMonth'\r\n\t| 'ExpYear'\r\n\t| 'Expires'\r\n\t| 'Expired'\r\n\t| 'SecurityCode'\r\n\t| 'Submit'\r\n\t| 'Save'\r\n\t| 'ZipCode'\r\n\t| 'Remove'\r\n\t| 'Removed'\r\n\t| 'Update'\r\n\t| 'Undo'\r\n\t| 'Cancel'\r\n\t| 'Enter'\r\n\t| 'Close'\r\n\t| 'SortBy'\r\n\t| 'PleaseWait'\r\n\t| 'Unknown'\r\n\t| 'Yes'\r\n\t| 'No'\r\n\t| 'Delete'\r\n\t| 'Update'\r\n\t| 'SaveAndContinueButtonText'\r\n\t| 'SaveAndContinue'\r\n\t| 'Continue'\r\n\t| 'SaveErrorMessage'\r\n\t| 'Edit'\r\n\t| 'ErrorMessageGenericTitle'\r\n\t| 'CheckEnteredValues'\r\n\t| 'ErrorMessageForbidden'\r\n\t| 'ErrorMessageNetwork'\r\n\t| 'ErrorOccurredSubmittingTheForm'\r\n\t| 'UnexpectedErrorOccurred'\r\n\t| 'Login'\r\n\t| 'ReturnToShipping'\r\n\t| 'BreadcrumbsLabel'\r\n\t| 'Other'\r\n\t| 'RememberMe'\r\n\t| 'ForgotYourPassword'\r\n\t| 'RegisterNewUserLinkText'\r\n\t| 'LoginPageTitleText'\r\n\t| 'ForgotPasswordPageTitle'\r\n\t| 'ForgotPasswordConfirmationPageTitle'\r\n\t| 'ForgotPasswordConfirmation'\r\n\t| 'Optional'\r\n\t| 'SearchBoxPlaceholder'\r\n\t| 'XOutOfYStars'\r\n\t| 'XToYOfZ'\r\n\t| 'ModalLabel'\r\n\t| 'RequiredFields'\r\n\t| 'Status'\r\n\t| 'Version'\r\n\t| 'CreatedOn'\r\n\t| 'CreatedBy'\r\n\t| 'ModifiedOn'\r\n\t| 'ModifiedBy'\r\n\t| 'OpenStatusInformation'\r\n\t| 'CloseStatusInformation'\r\n\t| 'SearchFilter'\r\n\t| 'Pagination'\r\n\t| 'Previous'\r\n\t| 'Next'\r\n\t| 'ClearFilter';\r\n\r\n/**\r\n * Type that will hold all the localization keys\r\n */\r\nexport type LocalizerResourceKey =\r\n\t| LocalizerGlobalResourceKeys\r\n\t| LoginResourceKeys\r\n\t| ContactFormResourceKeys\r\n\t// #region Commerce\r\n\t| CommerceResourceKeys\r\n\t// #endregion\r\n\t| ResetPasswordResourceKeys\r\n\t| CreatePasswordResourceKeys\r\n\t| SubmitQuestionResourceKeys\r\n\t| ResetPasswordResourceKeys\r\n\t| CreatePasswordResourceKeys\r\n\t| MyAccountResourceKeys\r\n\t| LoginWith2faResourceKeys\r\n\t| LoginWithRecoveryCodeResourceKeys\r\n\t| AddressResourceKeys\r\n\t// #region HCL\r\n\t| QuickBagsResourceKeys\r\n\t| QuickLabelsResourceKeys\r\n\t| ProductCatalogsResourceKeys\r\n\t| NewsletterKeys\r\n\t| FileUploadResourceKeys;\r\n// #endregion\r\n\r\n/**\r\n * Interface for the resources\r\n *\r\n * @export\r\n * @interface LocalizerResource\r\n */\r\ninterface LocalizerResource {\r\n\t[id: string]: string;\r\n}\r\n\r\n/**\r\n * Extends Window with a property that will hold the localizer resources\r\n *\r\n * @interface WindowWithResources\r\n * @extends {Window}\r\n */\r\nexport interface WindowWithResources extends Window {\r\n\t__ROC_LOCALIZER_RESOURCES__?: LocalizerResource[];\r\n}\r\n\r\n/**\r\n * Main object that will hold all the localized resources\r\n */\r\nconst LocalizerResources: LocalizerResource = {};\r\n\r\n/**\r\n * To prevent redundant logging\r\n */\r\nconst isAlreadyLogged: Record = {};\r\n\r\n/**\r\n * Initializer function that will populate the main localized resources object\r\n */\r\nfunction initializeLocalizerResources() {\r\n\tconst windowWithResources = window as WindowWithResources;\r\n\r\n\tif (\r\n\t\twindowWithResources &&\r\n\t\twindowWithResources.__ROC_LOCALIZER_RESOURCES__ &&\r\n\t\tArray.isArray(windowWithResources.__ROC_LOCALIZER_RESOURCES__)\r\n\t) {\r\n\t\twindowWithResources.__ROC_LOCALIZER_RESOURCES__.forEach((obj) => {\r\n\t\t\tfor (const key of Object.keys(obj)) {\r\n\t\t\t\tLocalizerResources[key] = obj[key];\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n}\r\n\r\n/**\r\n * Function that can be used to load the localized strings with support to parameter replacements\r\n *\r\n * @export\r\n * @param {string} key\r\n * @param {...string[]} replaceStrings\r\n * @returns\r\n */\r\nexport function Localizer(\r\n\tkey: TResourceKey,\r\n\t...replaceStrings: string[]\r\n) {\r\n\tif (Object.keys(LocalizerResources).length === 0) {\r\n\t\tinitializeLocalizerResources();\r\n\t}\r\n\r\n\tif (!LocalizerResources || !LocalizerResources[key]) {\r\n\t\t// if running in a test environment, just skip any warnings.\r\n\t\tif (process.env.NODE_ENV !== 'test') {\r\n\t\t\tif (!isAlreadyLogged[key]) {\r\n\t\t\t\tconsole.warn(key, 'Localizer Resource not found. Key will be used instead.');\r\n\t\t\t\tisAlreadyLogged[key] = true;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn key;\r\n\t}\r\n\r\n\tconst resource = LocalizerResources[key];\r\n\r\n\tif (!replaceStrings) {\r\n\t\treturn resource;\r\n\t}\r\n\r\n\treturn resource.replace(/{(\\d+)}/g, (match: string, index: number) => {\r\n\t\treturn replaceStrings[index] ? replaceStrings[index] : '';\r\n\t});\r\n}\r\n\r\n/**\r\n * Interface for the LocalizedSpan component\r\n *\r\n * @interface LocalizedSpanProps\r\n */\r\ninterface LocalizedSpanProps {\r\n\tresourceKey: LocalizerResourceKey;\r\n\targs?: string[];\r\n}\r\n\r\n/**\r\n * Component responsible for the initialization of the react app\r\n */\r\nexport function LocalizedSpan(props: LocalizedSpanProps) {\r\n\tconst { resourceKey, args = [] } = props;\r\n\tconst localizedString = Localizer(resourceKey, ...args);\r\n\r\n\treturn (\r\n\t\t\r\n\t);\r\n}\r\n","export interface Asset {\r\n\t/**\r\n\t * The asset's type\r\n\t */\r\n\tassetType: AssetType;\r\n\t/**\r\n\t * The asset's url\r\n\t */\r\n\tassetUrl: string;\r\n}\r\n\r\nexport enum AssetType {\r\n\tImage = 'Image',\r\n\tMedia = 'Media',\r\n\tDocument = 'Document',\r\n\tYouTube = 'YouTube',\r\n}\r\n\r\n/**\r\n * Image Dimension\r\n */\r\nexport interface AssetDimension {\r\n\t/**\r\n\t * The label of image dimension\r\n\t */\r\n\tLabel: string;\r\n\tWidth: number;\r\n\tHeight: number;\r\n}\r\n","// source: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent\r\n(function () {\r\n\tif (typeof window.CustomEvent === 'function') {\r\n\t\treturn false;\r\n\t}\r\n\r\n\tfunction CustomEvent(event, params) {\r\n\t\tparams = params || { bubbles: false, cancelable: false, detail: undefined };\r\n\t\t// eslint-disable-next-line\r\n\t\tvar evt = document.createEvent('CustomEvent');\r\n\t\tevt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);\r\n\t\treturn evt;\r\n\t}\r\n\r\n\tCustomEvent.prototype = window.Event.prototype;\r\n\r\n\twindow.CustomEvent = CustomEvent;\r\n})();\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * Left chevron SVG\r\n *\r\n * @returns\r\n */\r\nfunction LeftChevronSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default LeftChevronSVG;\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * Up chevron SVG\r\n *\r\n * @returns\r\n */\r\nfunction DownChevronSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default DownChevronSVG;\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * Up chevron SVG\r\n *\r\n * @returns\r\n */\r\nfunction UpChevronSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default UpChevronSVG;\r\n","import { useInView, IntersectionOptions, InViewHookResponse } from 'react-intersection-observer';\r\nimport { useEffect, useState } from 'react';\r\n\r\n/**\r\n * Wrapper hook to useInView so that it only triggers once. Useful for lazy-loaded content that\r\n * doesn't need to change once it's out of view.\r\n */\r\nexport function useInViewOnce(options: IntersectionOptions = {}): InViewHookResponse {\r\n\tconst inViewHookResponse = useInView(options);\r\n\tconst [viewed, setViewed] = useState(false);\r\n\r\n\tuseEffect(() => {\r\n\t\tif (inViewHookResponse.inView) {\r\n\t\t\tsetViewed(true);\r\n\t\t}\r\n\t}, [inViewHookResponse.inView]);\r\n\r\n\tconst newResponse: InViewHookResponse = {\r\n\t\t...inViewHookResponse,\r\n\t\tinView: viewed,\r\n\t\t[0]: inViewHookResponse[0],\r\n\t\t[1]: viewed,\r\n\t\t[2]: inViewHookResponse[2],\r\n\t};\r\n\r\n\tfunction* makeIterator() {\r\n\t\tyield newResponse[0];\r\n\t\tyield newResponse[1];\r\n\t\tyield newResponse[2];\r\n\t}\r\n\r\n\tnewResponse[Symbol.iterator] = makeIterator;\r\n\r\n\treturn newResponse;\r\n}\r\n","import classnames from 'classnames';\r\nimport React, { ImgHTMLAttributes, useEffect, useState } from 'react';\r\nimport { useInViewOnce } from 'shared/hooks/useInViewOnce';\r\n\r\ninterface LazyImageProps extends ImgHTMLAttributes {\r\n\tplaceholderImg?: string;\r\n\tbackgroundImg?: boolean;\r\n\tclassName?: string;\r\n\tinactiveClassName?: string;\r\n\tloadedClassName?: string;\r\n\tdelay?: number;\r\n\talt: string;\r\n\tsrc: string;\r\n}\r\n\r\n/**\r\n * Component that will render an tag that will have its source image lazily loaded only once\r\n * the element is visible in the viewport.\r\n * @param props\r\n */\r\nexport default function LazyImage({\r\n\tplaceholderImg = '/images/placeholder/placeholder-image.png',\r\n\tsrc,\r\n\talt,\r\n\tbackgroundImg = false,\r\n\tdelay = 0,\r\n\tclassName,\r\n\tinactiveClassName = 'roc-lazy-image--inactive',\r\n\tloadedClassName = 'roc-lazy-image--loaded',\r\n\t...rest\r\n}: LazyImageProps) {\r\n\tconst [ref, viewed] = useInViewOnce({\r\n\t\tthreshold: 0,\r\n\t});\r\n\r\n\tconst [loadedClassDisplayed, setLoadedClassDisplayed] = useState(false);\r\n\r\n\tuseEffect(() => {\r\n\t\tif (viewed) {\r\n\t\t\tsetTimeout(() => {\r\n\t\t\t\tsetLoadedClassDisplayed(true);\r\n\t\t\t}, delay);\r\n\t\t}\r\n\t}, [viewed, delay]);\r\n\r\n\tconst imageSrc = viewed ? src : placeholderImg;\r\n\r\n\tconst classes = classnames(className, {\r\n\t\t[inactiveClassName]: !viewed,\r\n\t\t[loadedClassName]: loadedClassDisplayed,\r\n\t});\r\n\r\n\tif (backgroundImg) {\r\n\t\treturn
;\r\n\t} else {\r\n\t\treturn {alt};\r\n\t}\r\n}\r\n","// These functions may be potentially innaccurate in certain locales that use differing amounts of characters in different cases\r\n\r\n/**\r\n * Tests if a string includes another if casing is ignored\r\n * @param thisString The string to test\r\n * @param otherString The potentially included string being tested for\r\n */\r\nexport function stringWeaklyIncludes(thisString: string, otherString: string) {\r\n\treturn thisString.toLocaleLowerCase().includes(otherString.toLocaleLowerCase());\r\n}\r\n\r\n/**\r\n * Tests if a string starts with another if casing is ignored\r\n * @param thisString The string to test\r\n * @param otherString The potentially included string being tested for\r\n */\r\nexport function stringWeaklyStartsWith(thisString: string, otherString: string) {\r\n\treturn thisString.toLocaleLowerCase().startsWith(otherString.toLocaleLowerCase());\r\n}\r\n\r\n/**\r\n * Tests if a string ends with another if casing is ignored\r\n * @param thisString The string to test\r\n * @param otherString The potentially included string being tested for\r\n */\r\nexport function stringWeaklyEndsWith(thisString: string, otherString: string) {\r\n\treturn thisString.toLocaleLowerCase().endsWith(otherString.toLocaleLowerCase());\r\n}\r\n\r\n/**\r\n * Converts the first character in a string to upper-case. Will not work for all languages (for example Dutch).\r\n *\r\n * @export\r\n * @param {string} thisString\r\n * @returns\r\n */\r\nexport function firstCharacterToUpper(thisString: string) {\r\n\tif (!thisString) {\r\n\t\treturn '';\r\n\t}\r\n\r\n\tlet newString = thisString.substring(0, 1).toLocaleUpperCase();\r\n\r\n\tif (thisString.length > 1) {\r\n\t\tnewString += thisString.substring(1);\r\n\t}\r\n\r\n\treturn newString;\r\n}\r\n\r\n/**\r\n * Converts the first character in a string to upper-case. Will not work for all languages (for example Dutch).\r\n *\r\n * @export\r\n * @param {string} thisString\r\n * @returns\r\n */\r\nexport function firstCharacterToLower(thisString: string) {\r\n\tif (!thisString) {\r\n\t\treturn '';\r\n\t}\r\n\r\n\tlet newString = thisString.substring(0, 1).toLocaleLowerCase();\r\n\r\n\tif (thisString.length > 1) {\r\n\t\tnewString += thisString.substring(1);\r\n\t}\r\n\r\n\treturn newString;\r\n}\r\n\r\n// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js\r\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart\r\nexport function padStart(value, targetLength, padString) {\r\n\t// tslint:disable-next-line:no-bitwise\r\n\ttargetLength = targetLength >> 0; // truncate if number, or convert non-number to 0;\r\n\tpadString = String(typeof padString !== 'undefined' ? padString : ' ');\r\n\tif (value.length >= targetLength) {\r\n\t\treturn String(value);\r\n\t} else {\r\n\t\ttargetLength = targetLength - value.length;\r\n\t\tif (targetLength > padString.length) {\r\n\t\t\tpadString += padString.repeat(targetLength / padString.length); // append to original to ensure we are longer than needed\r\n\t\t}\r\n\t\treturn padString.slice(0, targetLength) + String(value);\r\n\t}\r\n}\r\n\r\n/**\r\n * Converts a string from \" Something Like This\" to \"something-like-this\"\r\n * @param value The string to convert\r\n */\r\nexport function toKebabCase(value: string) {\r\n\tif (!value || !value.trim()) {\r\n\t\treturn '';\r\n\t}\r\n\r\n\treturn value\r\n\t\t.trim()\r\n\t\t.replace(/[^a-zA-Z0-9]+/g, '-')\r\n\t\t.replace(/([a-z0-9])([A-Z])/g, '$1-$2')\r\n\t\t.replace(/([A-Z])([A-Z])(?=[a-z])/g, '$1-$2')\r\n\t\t.replace(/-$/, '')\r\n\t\t.toLocaleLowerCase();\r\n}\r\n\r\n/**\r\n * Converts a string from PascalCase, camelCase, snake_case, or space separated to camelCase. Beginning and trailing whitespace are trimmed.\r\n * @param value The string to convert\r\n */\r\nexport function toCamelCase(value: string) {\r\n\treturn value\r\n\t\t.replace(/(?:^\\w|[A-Z]|\\b\\w)/g, function (word, index) {\r\n\t\t\treturn index === 0 ? word.toLowerCase() : word.toUpperCase();\r\n\t\t})\r\n\t\t.replace(/(?:\\s+|[-_])/g, '');\r\n}\r\n\r\n/**\r\n * Removes html characters\r\n *\r\n * @export\r\n * @param {*} html\r\n * @returns\r\n */\r\nexport function stripHtml(html) {\r\n\tconst tmp = document.createElement('div');\r\n\ttmp.innerHTML = html;\r\n\treturn tmp.textContent || tmp.innerText || '';\r\n}\r\n\r\n/**\r\n * Cuts of text and adds ellipsis (...) after a certain number of characters\r\n * @param text The text to format\r\n * @param cutoff The number of characters to add the ellipsis after\r\n */\r\nexport function addEllipsis(text: string, cutoff: number = 20) {\r\n\tif (!text || text.length < cutoff) {\r\n\t\treturn text;\r\n\t}\r\n\r\n\treturn text.substring(0, cutoff) + '...';\r\n}\r\n\r\n/**\r\n * Trims a string value\r\n */\r\nexport function trimString(value: string) {\r\n\treturn value ? value.trim() : '';\r\n}\r\n","import { ExternalLoginProvider } from 'features/login/ExternalLogins';\r\n\r\n/**\r\n * Extends Window with a property that will hold the client data\r\n *\r\n * @interface WindowWithClientData\r\n * @extends {Window}\r\n */\r\ninterface WindowWithServerData extends Window {\r\n\t__ROC_SERVER_DATA__?: Array<{\r\n\t\t[name: string]: string;\r\n\t}>;\r\n}\r\n\r\n/**\r\n * This type will hold all the keys that are used by ServerData\r\n */\r\ninterface ServerData {\r\n\tIsReCaptchaEnabled?: boolean;\r\n\tReCaptchaV3Key?: string;\r\n\tTimeZone?: string;\r\n\tUSE_EMAIL_AS_USERNAME?: boolean;\r\n\tRegistrationThankYouTitle?: string;\r\n\tRegistrationThankYouMessage?: string;\r\n\tForgotPasswordConfirmationTitle?: string;\r\n\tForgotPasswordConfirmationMessage?: string;\r\n\tPageTitleSuffix?: string;\r\n\tRequireConfirmEmail?: boolean;\r\n\tACCOUNT_URL_BASE?: string;\r\n\tACCOUNT_LOGIN_URL?: string;\r\n\tACCOUNT_LOGOUT_URL?: string;\r\n\tACCOUNT_REGISTER_URL?: string;\r\n\tACCOUNT_LOGIN_WITH_2FA_URL?: string;\r\n\tACCOUNT_FORGOT_PASSWORD_URL?: string;\r\n\tACCOUNT_LOGIN_WITH_RECOVERY_CODE_URL?: string;\r\n\tEXTERNAL_LOGIN_PROVIDERS?: ExternalLoginProvider[];\r\n\t// #region Commerce\r\n\tACCOUNT_ORGANIZATION_REVIEW_URL?: string;\r\n\tACCOUNT_ORGANIZATION_SELECTOR_URL?: string;\r\n\tGoogleMapsApiKey?: string; // used in Store Locator\r\n\tMinimumTimeBeforeRecentlyViewedProductsAreValidated?: number;\r\n\tQuickOrderItemMinumumSearchCharacters?: number;\r\n\tMaxAllowedQuickOrderItems?: number;\r\n\tMaxNumberForQuantityDropdown?: number;\r\n\tOrderDetailsUIComponent?: string;\r\n\tGuestCheckoutEnabled?: boolean;\r\n\t// #endregion\r\n}\r\n\r\n/**\r\n * Main object that will hold all the client data\r\n */\r\nexport const ServerData: ServerData = {};\r\n\r\n/**\r\n * Initializer function that will populate the main settings object\r\n */\r\nfunction initializeServerData() {\r\n\tconst windowWithServerData = window as WindowWithServerData;\r\n\r\n\tif (\r\n\t\twindowWithServerData &&\r\n\t\twindowWithServerData.__ROC_SERVER_DATA__ &&\r\n\t\tArray.isArray(windowWithServerData.__ROC_SERVER_DATA__)\r\n\t) {\r\n\t\tfor (const obj of windowWithServerData.__ROC_SERVER_DATA__) {\r\n\t\t\tfor (const key of Object.keys(obj)) {\r\n\t\t\t\tServerData[key] = obj[key];\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\ninitializeServerData();\r\n","/**\r\n * THIS HAS A COPY ON ROC.ADMIN. DO NOT MAKE CHANGES HERE WITHOUT ALSO MAKING THEM IN THE OTHER FILE.\r\n */\r\n\r\nimport { Prices, PricingResult } from 'shared-client/types/commerce/pricing-result';\r\n\r\n/**\r\n * Splits a quantity range of format `\"min:max\"` into a two dimensional array in the form of `[min: number, max: number]`.\r\n * It is illegal for quantity ranges to be fractional.\r\n * @param quantityRange A quantity range string in the form of `\"min:max\"`\r\n */\r\nexport function ParseQuantityRange(quantityRange: string): [number, number] {\r\n\tconst s = quantityRange.split(':');\r\n\treturn [parseInt(s[0]), s[1] === 'Infinity' ? Infinity : parseInt(s[1])];\r\n}\r\n\r\n/**\r\n * Retrieves a price detail of type T from a quantity range index for the specified quantity.\r\n * For example an index with the values `{[1:49]:\"$4.99\", [50:Infinity]:\"$3.99\"}` would return\r\n * \"$4.99\" for the quantity 1 or 25 but \"3.99\" for quantities above 49.\r\n * @param prices\r\n * @param quantity\r\n */\r\nexport function GetPriceForQuantity(prices: Prices, quantity: number): T {\r\n\tif (quantity == null || quantity <= 0) {\r\n\t\tthrow new Error('Erroneous quantity passed to GetPriceForQuantity');\r\n\t}\r\n\r\n\tfor (const quantityRange in prices) {\r\n\t\tconst [min, max] = ParseQuantityRange(quantityRange);\r\n\t\tif (quantity >= min && quantity <= max) {\r\n\t\t\treturn prices[quantityRange];\r\n\t\t}\r\n\t}\r\n\r\n\tthrow new Error('Price not found. Is the prices object well formed?');\r\n}\r\n\r\n/** Retrieves a pricing line for a given pricing result by the specified pricing line ID */\r\nexport function GetPricingLine(pricingResult: PricingResult, productId: string, pricingLineId?: string | null) {\r\n\tconst line = pricingResult.products[productId];\r\n\tif (!line) {\r\n\t\treturn undefined;\r\n\t}\r\n\r\n\tif (pricingLineId == null) {\r\n\t\tfor (const id in line.pricingLines) {\r\n\t\t\tconst pricingLine = line.pricingLines[id];\r\n\t\t\tif (pricingLine && pricingLine.isDefault) {\r\n\t\t\t\treturn pricingLine;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tfor (const id in line.pricingLines) {\r\n\t\t\tconst pricingLine = line.pricingLines[id];\r\n\t\t\tif (pricingLine) {\r\n\t\t\t\treturn pricingLine;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn undefined;\r\n\t}\r\n\r\n\treturn line.pricingLines[pricingLineId];\r\n}\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * Question Mark within a Circle SVG\r\n */\r\nfunction QuestionMarkCircleSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default QuestionMarkCircleSVG;\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * Right chevron SVG\r\n *\r\n * @returns\r\n */\r\nfunction RightChevronSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default RightChevronSVG;\r\n","import React from 'react';\r\nimport useDelayedDisplay from 'shared/hooks/useDelayedDisplay';\r\nimport classnames from 'classnames';\r\n\r\ntype SpinnerAlignment = 'left' | 'right' | 'none';\r\n\r\ninterface SpinnerProps {\r\n\tlight?: boolean;\r\n\twaitMs?: number;\r\n\talign?: SpinnerAlignment;\r\n\tblock?: boolean;\r\n\tparentCentered?: boolean;\r\n}\r\n\r\n/**\r\n * Reusable roc-spinner component with color, alignment and wait time props.\r\n *\r\n * @export\r\n * @param {SpinnerProps} { light = false, waitMs = 200, align = 'right', block }\r\n * @returns\r\n */\r\nexport default function Spinner({\r\n\tlight = false,\r\n\twaitMs = 200,\r\n\talign = 'right',\r\n\tblock,\r\n\tparentCentered = false,\r\n}: SpinnerProps) {\r\n\tconst display = useDelayedDisplay(waitMs);\r\n\r\n\tif (!display) {\r\n\t\treturn null;\r\n\t}\r\n\r\n\tconst spinner = (\r\n\t\t\r\n\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t
\r\n\t\t
\r\n\t);\r\n\r\n\tif (parentCentered) {\r\n\t\treturn
{spinner}
;\r\n\t}\r\n\r\n\treturn spinner;\r\n}\r\n","import AxiosHelper from 'services/AxiosHelper';\r\nimport { BasePartialDataOptions } from 'shared/types';\r\nimport { BaseBulkPartialProductsDataOptions, ProductDetailsState } from './types';\r\n\r\nconst partialDataCache: { [index: string]: Partial } = {};\r\nconst partialProductsDataCache: { [index: string]: BulkProductsPartialDataResponse } = {};\r\n\r\nexport async function getPartialProductData(\r\n\toptions: BasePartialDataOptions,\r\n) {\r\n\tlet cacheKey = `${options.id}_${options.missingKeys.sort().join('_')}`;\r\n\r\n\t// include additional params in the cache key and preserve their order by param name\r\n\tconst additionalParams = options.additionalParams;\r\n\tif (additionalParams) {\r\n\t\tconst additionalKeys = Object.keys(additionalParams).sort();\r\n\t\tadditionalKeys.forEach((name) => {\r\n\t\t\tconst paramValue = additionalParams[name];\r\n\t\t\tif (paramValue) {\r\n\t\t\t\tcacheKey += `_${name}=${paramValue}`;\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\tif (partialDataCache[cacheKey]) {\r\n\t\treturn Promise.resolve(partialDataCache[cacheKey]);\r\n\t}\r\n\r\n\tconst response = await AxiosHelper.get>(`/ajax/products/${options.id}`, {\r\n\t\tparams: {\r\n\t\t\t...options.additionalParams,\r\n\t\t\tkeys: options.missingKeys.join(','),\r\n\t\t},\r\n\t});\r\n\r\n\tpartialDataCache[cacheKey] = response.data;\r\n\r\n\t// if the key is requested, always return the value as null. this is important\r\n\t// on the client undefined vs null can have different meanings\r\n\t// undefined means nothing has even tried to set it, the data is simply missing.\r\n\t// null means we tried to load something, but there's no value associated.\r\n\treturn Promise.resolve(response.data);\r\n}\r\n\r\n/**\r\n * Dictionary of bulk products data , key is product id\r\n */\r\nexport interface BulkProductsPartialDataResponse {\r\n\t[key: string]: Partial;\r\n}\r\n\r\n/**\r\n * Gets partial data for bulk products using the product ids and the missing keys\r\n */\r\nexport async function getBulkPartialProductsData(options: BaseBulkPartialProductsDataOptions) {\r\n\tconst cacheKey = `${options.productIds?.sort().join('_')}___${options.missingKeys.sort().join('_')}`;\r\n\tif (partialProductsDataCache[cacheKey]) {\r\n\t\treturn Promise.resolve(partialProductsDataCache[cacheKey]);\r\n\t}\r\n\r\n\tconst response = await AxiosHelper.post(`/ajax/products`, {\r\n\t\tproductIds: options.productIds,\r\n\t\tmissingKeys: options.missingKeys.join(','),\r\n\t});\r\n\r\n\tpartialProductsDataCache[cacheKey] = response.data;\r\n\r\n\t// if the key is requested, always return the value as null. this is important\r\n\t// on the client undefined vs null can have different meanings\r\n\t// undefined means nothing has even tried to set it, the data is simply missing.\r\n\t// null means we tried to load something, but there's no value associated.\r\n\treturn Promise.resolve(response.data);\r\n}\r\n","// Extra large desktop breakpoint.\r\nexport const xlg = 1200;\r\n\r\n// Desktop breakpoint.\r\nexport const lg = 992;\r\n\r\n// Tablet breakpoint.\r\nexport const md = 768;\r\n\r\n// Mobile breakpoint.\r\nexport const sm = 576;\r\n","import axios, { AxiosInstance, AxiosResponse, AxiosError } from 'axios';\r\nimport AccountUrls from 'shared/util/AccountUrls';\r\nimport { getCsrfToken } from './UserData';\r\n\r\n/**\r\n * Helper that creates an instance of Axios to be used throughout the client application\r\n * and adds a default X-Requested-With header to all requests.\r\n *\r\n */\r\nconst AxiosHelper: AxiosInstance = axios.create();\r\nAxiosHelper.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';\r\n\r\n// Add CSRF tokens to all commands that are not GET, HEAD, or OPTIONS\r\nAxiosHelper.interceptors.request.use(async (config) => {\r\n\tif (\r\n\t\tconfig.method?.toLowerCase() !== 'get' &&\r\n\t\tconfig.method?.toLowerCase() !== 'head' &&\r\n\t\tconfig.method?.toLowerCase() !== 'options'\r\n\t) {\r\n\t\tconst csrfToken = await getCsrfToken();\r\n\t\tconfig.headers = { RequestVerificationToken: csrfToken, ...config.headers };\r\n\t}\r\n\treturn config;\r\n});\r\n\r\nexport default AxiosHelper;\r\n\r\n// eslint-disable-next-line no-unused-vars\r\nexport const addUnauthorizedAccessInterceptor = (history) => {\r\n\t// this allows us to act when any request going through axios succeeds or fails.\r\n\tAxiosHelper.interceptors.response.use(\r\n\t\t(response: AxiosResponse) => {\r\n\t\t\treturn response;\r\n\t\t},\r\n\t\t(error: AxiosError) => {\r\n\t\t\tif (error.response && error.response.status === 401) {\r\n\t\t\t\twindow.location.replace(\r\n\t\t\t\t\t`${AccountUrls.Login}?ReturnUrl=${encodeURIComponent(window.location.pathname)}`,\r\n\t\t\t\t);\r\n\t\t\t}\r\n\r\n\t\t\treturn Promise.reject(error);\r\n\t\t},\r\n\t);\r\n};\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * Play Media Button SVG\r\n */\r\nfunction PlayMediaSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default PlayMediaSVG;\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * PDF file SVG\r\n */\r\nfunction FilePDFSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default FilePDFSVG;\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * Text file SVG\r\n */\r\nfunction FileWithTextSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default FileWithTextSVG;\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * Word Document file SVG\r\n */\r\nfunction FileWordDocSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default FileWordDocSVG;\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * Excel file SVG\r\n */\r\nfunction FileExcelSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default FileExcelSVG;\r\n","import React from 'react';\r\nimport { Asset, AssetType } from './types';\r\nimport PlayMediaSVG from 'shared/components/svg/PlayMediaSVG';\r\nimport FilePDFSVG from 'shared/components/svg/FilePDFSVG';\r\nimport FileWithTextSVG from 'shared/components/svg/FileWithTextSVG';\r\nimport FileWordDocSVG from 'shared/components/svg/FileWordDocSVG';\r\nimport FileExcelSVG from 'shared/components/svg/FileExcelSVG';\r\nimport FileSVG from 'shared/components/svg/FileSVG';\r\nimport QuestionMarkCircleSVG from 'shared/components/svg/QuestionMarkCircleSVG';\r\n\r\ninterface AssetDownloadIconProps {\r\n\tasset: Asset;\r\n\tassetIconClasses?: string;\r\n}\r\n\r\n/**\r\n * Icon for an asset based on its file type.\r\n */\r\nexport default function AssetIcon(props: AssetDownloadIconProps) {\r\n\tconst { asset, assetIconClasses } = props;\r\n\r\n\tif (asset.assetType === AssetType.Media || asset.assetType === AssetType.YouTube) {\r\n\t\treturn ;\r\n\t} else if (asset.assetType === AssetType.Document) {\r\n\t\tconst fileExtension = asset.assetUrl.split('.').pop();\r\n\r\n\t\tswitch (fileExtension) {\r\n\t\t\tcase 'pdf':\r\n\t\t\t\treturn ;\r\n\t\t\tcase 'txt':\r\n\t\t\tcase 'html':\r\n\t\t\tcase 'htm':\r\n\t\t\tcase 'xml':\r\n\t\t\t\treturn ;\r\n\t\t\tcase 'docx':\r\n\t\t\tcase 'doc':\r\n\t\t\tcase 'docm':\r\n\t\t\tcase 'dot':\r\n\t\t\tcase 'dotm':\r\n\t\t\tcase 'dotx':\r\n\t\t\t\treturn ;\r\n\t\t\tcase 'csv':\r\n\t\t\tcase 'xls':\r\n\t\t\tcase 'xlsx':\r\n\t\t\tcase 'xlsb':\r\n\t\t\tcase 'xlsm':\r\n\t\t\tcase 'xlt':\r\n\t\t\tcase 'xltm':\r\n\t\t\tcase 'xltx':\r\n\t\t\tcase 'xlw':\r\n\t\t\t\treturn ;\r\n\t\t\tdefault:\r\n\t\t\t\treturn ;\r\n\t\t}\r\n\t} else {\r\n\t\treturn ;\r\n\t}\r\n}\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * Download SVG\r\n *\r\n * @returns\r\n */\r\nfunction DownloadSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default DownloadSVG;\r\n","import React from 'react';\r\nimport { Asset } from './types';\r\nimport AssetIcon from './AssetIcon';\r\nimport DownloadSVG from 'shared/components/svg/DownloadSVG';\r\n\r\ninterface AssetDownloadIconProps {\r\n\tasset: Asset;\r\n\tassetIconClasses?: string;\r\n\tdownloadIconClasses?: string;\r\n}\r\n\r\n/**\r\n * Icon for downloading assets.\r\n */\r\nexport default function AssetDownloadIcon(props: AssetDownloadIconProps) {\r\n\tconst { asset, assetIconClasses, downloadIconClasses } = props;\r\n\r\n\treturn (\r\n\t\t<>\r\n\t\t\t\r\n\t\t\t
\r\n\t\t\t\t\r\n\t\t\t
\r\n\t\t\r\n\t);\r\n}\r\n","import React from 'react';\r\n\r\ninterface ExternalMediaIFrameProps {\r\n\tembeddedUrl: string;\r\n\ttitle: string;\r\n}\r\n/**\r\n * This component display external media videos on front end\r\n */\r\nexport default function ExternalMediaIFrame(props: ExternalMediaIFrameProps) {\r\n\tconst { embeddedUrl, title } = props;\r\n\treturn (\r\n\t\t
\r\n\t\t\t\r\n\t\t
\r\n\t);\r\n}\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * HCL Zoom SVG\r\n *\r\n * @returns\r\n */\r\nfunction HclZoomSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default HclZoomSVG;\r\n","import React from 'react';\r\nimport type { Dialog } from '@reach/dialog';\r\nimport { Localizer } from 'services/Localizer';\r\nimport XSVG from 'shared/components/svg/XSVG';\r\nimport Spinner from 'shared/components/Spinner';\r\nconst LazyDialog = React.lazy(() =>\r\n\timport('@reach/dialog').then((module) => ({ default: module.Dialog })),\r\n);\r\n\r\ninterface ModalProps {\r\n\tisOpen: boolean;\r\n\tcloseModal: () => void;\r\n\tchildren?: React.ReactNode;\r\n}\r\n\r\nexport default function ProductImageModal(props: ModalProps) {\r\n\tconst { isOpen, closeModal, children } = props;\r\n\r\n\treturn (\r\n\t\t}>\r\n\t\t\t closeModal()}\r\n\t\t\t\taria-label={Localizer('ProductImageModal')}\r\n\t\t\t\tas={'div'}\r\n\t\t\t>\r\n\t\t\t\t
\r\n\t\t\t\t\t
\r\n\r\n\t\t\t\t\t
\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t
\r\n\t\t\t\t
\r\n\r\n\t\t\t\t
{children}
\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n","import classnames from 'classnames';\r\nimport React, { useEffect, useRef, useState } from 'react';\r\nimport type MediaQuery from 'react-responsive';\r\nimport AssetDownloadIcon from 'shared/assets/AssetDownloadIcon';\r\nimport AssetIcon from 'shared/assets/AssetIcon';\r\nimport ExternalMediaIFrame from 'shared/assets/ExternalMediaIFrame';\r\nimport LazyImage from 'shared/assets/LazyImage';\r\nimport { AssetType } from 'shared/assets/types';\r\nimport DownChevronSVG from 'shared/components/svg/DownChevronSVG';\r\n// #region HCL\r\nimport HclZoomSVG from 'shared/components/svg/HclZoomSVG';\r\n//#endregion\r\nimport LeftChevronSVG from 'shared/components/svg/LeftChevronSVG';\r\nimport RightChevronSVG from 'shared/components/svg/RightChevronSVG';\r\nimport UpChevronSVG from 'shared/components/svg/UpChevronSVG';\r\nimport useSmoothScrollingPolyfill from 'shared/hooks/useSmoothScrollingPolyfill';\r\nimport ProductImageModal from './ProductImageModal';\r\nimport Spinner from 'shared/components/Spinner';\r\nconst LazyMediaQuery = React.lazy(() => import('react-responsive'));\r\n\r\n// #region HCL\r\nimport { ProductAsset } from './types';\r\nimport { Localizer } from 'services/Localizer';\r\n// #endregion\r\n\r\ninterface ProductAssetsProps {\r\n\tassets?: ProductAsset[] | null;\r\n\t// #region HCL\r\n\tsaleDiscount: number;\r\n\tisNewProduct: boolean;\r\n\t//#endregion\r\n}\r\n\r\nexport const productAssetPlaceholder: ProductAsset = {\r\n\taltText: 'Image not available',\r\n\tsortOrder: 1,\r\n\tthumbnailUrl: '/images/image-not-found-thumb.png',\r\n\tmainImageUrl: '/images/image-not-found.png',\r\n\tenlargedImageUrl: '/images/image-not-found.png',\r\n\tassetType: AssetType.Image,\r\n\tassetUrl: '/images/image-not-found.png',\r\n\tembeddedUrl: '',\r\n};\r\n\r\n/**\r\n * Returns the first asset from the set of provided assets or null if no default found\r\n */\r\nfunction initMainProductAsset(assets?: ProductAsset[] | null): ProductAsset | null {\r\n\tif (assets == null || assets.length === 0) {\r\n\t\treturn null;\r\n\t}\r\n\tconst defaultAsset = assets\r\n\t\t.sort(compareAssets)\r\n\t\t.find((asset) => asset.assetType === AssetType.Image || asset.assetType === AssetType.YouTube);\r\n\treturn defaultAsset || null;\r\n}\r\n\r\n/**\r\n * Returns list of alternative assets (including the main asset).\r\n */\r\nfunction initAltAssets(assets?: ProductAsset[] | null): ProductAsset[] {\r\n\tif (!assets || assets === null || assets.length === 0) {\r\n\t\treturn [productAssetPlaceholder];\r\n\t}\r\n\treturn assets.sort(compareAssets);\r\n}\r\n\r\nconst compareAssets = (a: ProductAsset, b: ProductAsset) => a.sortOrder - b.sortOrder;\r\n\r\n/**\r\n * Displays assets for a product, including alternate assets, and handles swapping\r\n */\r\nexport default function ProductAssets(props: ProductAssetsProps) {\r\n\tconst [mainAsset, setMainAsset] = useState(productAssetPlaceholder);\r\n\tconst [altAssets, setAltAssets] = useState([]);\r\n\tconst [isImageSliderModalOpen, setIsImageSliderModalOpen] = useState(false);\r\n\r\n\tconst carousel = useRef(null);\r\n\r\n\tconst nodes = new Map();\r\n\tuseSmoothScrollingPolyfill();\r\n\r\n\tuseEffect(() => {\r\n\t\tconst newMainAsset = initMainProductAsset(props.assets);\r\n\r\n\t\tif (newMainAsset) {\r\n\t\t\tsetMainAsset(newMainAsset);\r\n\t\t} else {\r\n\t\t\tsetMainAsset(productAssetPlaceholder);\r\n\t\t}\r\n\r\n\t\tconst initialThumbnails = initAltAssets(props.assets);\r\n\t\t// #region HCL\r\n\t\tconst assetsWithoutDocs = initialThumbnails.filter((it) => it.assetType !== AssetType.Document);\r\n\t\t// #endregion\r\n\t\tsetAltAssets(assetsWithoutDocs);\r\n\t}, [props.assets]);\r\n\r\n\t/**\r\n\t * Replaces the main image with the provided one\r\n\t * @param e\r\n\t * @param asset\r\n\t */\r\n\tfunction changeMainImage(e: React.MouseEvent, asset: ProductAsset): void {\r\n\t\te.preventDefault();\r\n\t\tsetMainAsset(asset);\r\n\t}\r\n\r\n\t/**\r\n\t * Scrolsl the carousel\r\n\t *\r\n\t * @param {boolean} up\r\n\t * @returns\r\n\t */\r\n\tfunction scrollCarousel(up: boolean) {\r\n\t\tif (Array.from(nodes.values()).length === 0) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif (!carousel || !carousel.current) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst item = Array.from(nodes.values()).filter((node) => node != null)[0];\r\n\r\n\t\tif (window.innerWidth > 767) {\r\n\t\t\tcarousel.current.scrollBy(0, up ? -item.clientHeight : item.clientHeight);\r\n\t\t} else {\r\n\t\t\tcarousel.current.scrollBy(up ? -item.clientHeight : item.clientHeight, 0);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Handle previous button click\r\n\t *\r\n\t */\r\n\tfunction handlePreviousButton() {\r\n\t\tscrollCarousel(true);\r\n\t}\r\n\r\n\t/**\r\n\t * Handle next button click\r\n\t *\r\n\t */\r\n\tfunction handleNextButton() {\r\n\t\tscrollCarousel(false);\r\n\t}\r\n\r\n\tconst assetScroller = (\r\n\t\t
\r\n\t\t\t
\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t
    \r\n\t\t\t\t\t{altAssets.map((altAsset, i) => (\r\n\t\t\t\t\t\t nodes.set(i, c)}\r\n\t\t\t\t\t\t\tclassName=\"roc-pdp-asset-scroller__item\"\r\n\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t{altAsset.assetType === AssetType.Image || altAsset.assetType === AssetType.YouTube ? (\r\n\t\t\t\t\t\t\t\t changeMainImage(event, altAsset)}\r\n\t\t\t\t\t\t\t\t\taria-label=\"Select image\"\r\n\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t{altAsset.assetType === AssetType.Image ? (\r\n\t\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t) : (\r\n\t\t\t\t\t\t\t\t\t\t<>\r\n\t\t\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t) : (\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t))}\r\n\t\t\t\t
\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t
\r\n\t\t
\r\n\t);\r\n\r\n\tconst mainImageContent = (\r\n\t\t
\r\n\t\t\t{mainAsset.assetType !== AssetType.YouTube ? (\r\n\t\t\t\t<>\r\n\t\t\t\t\t{/* #region HCL */}\r\n\t\t\t\t\t{props.saleDiscount > 0 ? (\r\n\t\t\t\t\t\t
\r\n\t\t\t\t\t\t\t{Localizer('Sale')}\r\n\t\t\t\t\t\t\t{props.saleDiscount}%\r\n\t\t\t\t\t\t
\r\n\t\t\t\t\t) : null}\r\n\t\t\t\t\t{props.isNewProduct ? (\r\n\t\t\t\t\t\t
\r\n\t\t\t\t\t\t\t{Localizer('New')}\r\n\t\t\t\t\t\t
\r\n\t\t\t\t\t) : null}\r\n\t\t\t\t\t{/* #endregion */}\r\n\t\t\t\t\t
\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t
\r\n\t\t\t\t\t
\r\n\t\t\t\t\t\t
\r\n\t\t\t\t\t\t\t setIsImageSliderModalOpen(true)}\r\n\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t
\r\n\t\t\t\t\t\t\t\t\t{/* #region HCL */}\r\n\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t{/* #endregion */}\r\n\t\t\t\t\t\t\t\t
\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t
\r\n\t\t\t\t\t
\r\n\t\t\t\t\r\n\t\t\t) : (\r\n\t\t\t\t\r\n\t\t\t)}\r\n\t\t
\r\n\t);\r\n\r\n\treturn (\r\n\t\t}>\r\n\t\t\t<>\r\n\t\t\t\t{\r\n\t\t\t\t\t {\r\n\t\t\t\t\t\t\tsetIsImageSliderModalOpen(false);\r\n\t\t\t\t\t\t}}\r\n\t\t\t\t\t>\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t}\r\n\t\t\t\t
\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t{mainImageContent}\r\n\t\t\t\t\t\t{assetScroller}\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t{assetScroller}\r\n\t\t\t\t\t\t{mainImageContent}\r\n\t\t\t\t\t\r\n\t\t\t\t
\r\n\t\t\t\r\n\t\t
\r\n\t);\r\n}\r\n","import { useEffect } from 'react';\r\nimport smoothscroll from 'smoothscroll-polyfill';\r\n\r\nexport default function useSmoothScrollingPolyfill() {\r\n\tuseEffect(() => {\r\n\t\tsmoothscroll.polyfill();\r\n\t}, []);\r\n}\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * Generic file SVG\r\n */\r\nfunction FileSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default FileSVG;\r\n","import React from 'react';\r\nimport LazyImage from 'shared/assets/LazyImage';\r\nimport { AssetType } from 'shared/assets/types';\r\nimport ProductPricing from 'shared/components/commerce/ProductPricing';\r\nimport { toKebabCase } from 'util/StringUtil';\r\nimport { GetPriceForQuantity, GetPricingLine } from '../pricing/pricing-result';\r\nimport { productAssetPlaceholder } from '../product-details/ProductAssets';\r\nimport { ProductAsset } from '../product-details/types';\r\nimport { RecentlyViewedProduct as Model } from './useRecentlyViewedProducts';\r\nimport { dispatchRocEvent } from 'shared/hooks/useRocEventHandler';\r\n\r\ninterface RecentlyViewedProductProps {\r\n\tproduct: Model;\r\n\tuseTinySliderLazyLoading?: boolean;\r\n}\r\n\r\n/**\r\n * Renders a single product that was recently viewed\r\n */\r\nexport default function RecentlyViewedProduct(props: RecentlyViewedProductProps) {\r\n\tconst { product } = props;\r\n\tconst compareAssets = (a: ProductAsset, b: ProductAsset) => a.sortOrder - b.sortOrder;\r\n\tconst defaultAsset = product?.assets?.sort(compareAssets).find((asset) => asset.assetType === AssetType.Image);\r\n\tconst mainImage = defaultAsset ?? productAssetPlaceholder;\r\n\r\n\tlet customUrl: string = '#';\r\n\tif (product?.summary?.meta?.url) {\r\n\t\tcustomUrl = product.summary.meta.url;\r\n\t}\r\n\r\n\tconst line = product.displayPricing && GetPricingLine(product.displayPricing, product.id);\r\n\tconst displayPrice = line && GetPriceForQuantity(line.prices, 1);\r\n\r\n\treturn (\r\n\t\t
\r\n\t\t\t\r\n\t\t\t\t\tdispatchRocEvent('product-clicked', {\r\n\t\t\t\t\t\tproductId: product.id,\r\n\t\t\t\t\t\turl: customUrl,\r\n\t\t\t\t\t\tproductName: product.summary ? product.summary.name : '',\r\n\t\t\t\t\t\tproductGroupId: product.summary?.productGroupId ?? null,\r\n\t\t\t\t\t})\r\n\t\t\t\t}\r\n\t\t\t>\r\n\t\t\t\t{mainImage !== null ? (\r\n\t\t\t\t\t
\r\n\t\t\t\t\t\t{props.useTinySliderLazyLoading ? (\r\n\t\t\t\t\t\t\t// Use tiny-slider's built-in lazy image loading to lazily load the image\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t) : (\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t)}\r\n\t\t\t\t\t
\r\n\t\t\t\t) : null}\r\n\t\t\t\t{product.summary ? (\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t{product.summary.name}\r\n\t\t\t\t\t\r\n\t\t\t\t) : null}\r\n\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t
\r\n\t);\r\n}\r\n","import { ProductDetailsState } from '../product-details/types';\r\nimport { useEffect, useState } from 'react';\r\nimport AxiosHelper from 'services/AxiosHelper';\r\nimport { ServerData } from '../../../services/ServerData';\r\nimport { getPartialProductData } from '../product-details/data';\r\n\r\ntype ViewTracked = {\r\n\tlastViewed: number;\r\n};\r\n\r\ntype SparseProductDetailsState = Pick &\r\n\tPartial>;\r\n\r\nconst sparseProductDetailsStateKeys: (keyof SparseProductDetailsState)[] = [\r\n\t'summary',\r\n\t'assets',\r\n\t'pricingLines',\r\n\t'displayPricing',\r\n];\r\n\r\n/**\r\n * Recently viewed product\r\n */\r\nexport type RecentlyViewedProduct = SparseProductDetailsState & ViewTracked;\r\n\r\n/**\r\n * Use recently viewed products hook params\r\n */\r\ninterface UseRecentlyViewedProductsParams {\r\n\tcurrentProduct?: ProductDetailsState | null;\r\n\tactive?: boolean;\r\n}\r\n\r\n/**\r\n * Use recently viewed products context\r\n */\r\ninterface RecentlyViewedProductsContext {\r\n\tproducts: RecentlyViewedProduct[];\r\n\ttimestamp: number | null;\r\n}\r\n\r\nclass RecentlyViewedProductsRepository {\r\n\t/**\r\n\t * Recently viewed product local storage key name\r\n\t */\r\n\tprivate keyName: string = 'RocRecentlyViewedProductsContext';\r\n\r\n\t/**\r\n\t * Get all recently viewed products from the local storage, including those that may no longer be valid.\r\n\t */\r\n\tpublic getAllMayIncludeInvalid(): RecentlyViewedProduct[] {\r\n\t\tlet context: RecentlyViewedProductsContext | null = null;\r\n\t\ttry {\r\n\t\t\tconst stateStr = localStorage.getItem(this.keyName);\r\n\t\t\tif (stateStr !== null) {\r\n\t\t\t\tcontext = JSON.parse(stateStr);\r\n\t\t\t\tif (context && context.timestamp) {\r\n\t\t\t\t\tconst expirationTime = 1000 * 60 * 60 * 24 * 64; // 60 days in milliseconds\r\n\t\t\t\t\tif (new Date().valueOf() - context.timestamp >= expirationTime) {\r\n\t\t\t\t\t\tlocalStorage.removeItem(this.keyName); // remove data because it has expired\r\n\t\t\t\t\t\tcontext = null;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} catch (error) {\r\n\t\t\tconsole.warn('Error when getting recently viewed products from local storage.', error);\r\n\t\t}\r\n\r\n\t\tif (!context || context.products === null) {\r\n\t\t\treturn new Array();\r\n\t\t}\r\n\r\n\t\treturn context.products;\r\n\t}\r\n\r\n\t/**\r\n\t * Get all recently viewed products from the local storage, except those that the server deems are invalid.\r\n\t */\r\n\tpublic async getAllValidOnly(): Promise {\r\n\t\tconst validProducts = await this.filterOutInvalid(this.getAllMayIncludeInvalid());\r\n\r\n\t\tthis.setAll(validProducts);\r\n\r\n\t\treturn validProducts;\r\n\t}\r\n\r\n\t/**\r\n\t * Filter out invalid products from the passed in list by querying the server.\r\n\t */\r\n\tprivate async filterOutInvalid(products: RecentlyViewedProduct[]) {\r\n\t\ttry {\r\n\t\t\tconst now: number = new Date().valueOf();\r\n\t\t\t// Obtain timespan from settings; default to 1 hour in milliseconds.\r\n\t\t\tconst timeSpan: number = ServerData.MinimumTimeBeforeRecentlyViewedProductsAreValidated || 60 * 60 * 1000;\r\n\r\n\t\t\tconst outsideTimeSpan: RecentlyViewedProduct[] = products.filter((p) => now - p.lastViewed > timeSpan);\r\n\t\t\tconst withinTimeSpan: RecentlyViewedProduct[] = products.filter((p) => now - p.lastViewed <= timeSpan);\r\n\r\n\t\t\tif (outsideTimeSpan.length > 0) {\r\n\t\t\t\tconst validProductIds: string[] = (\r\n\t\t\t\t\tawait AxiosHelper.get('/ajax/commerce/products/recently-viewed/filter-out-invalid', {\r\n\t\t\t\t\t\tparams: { productIds: outsideTimeSpan.map((product) => product.id) },\r\n\t\t\t\t\t})\r\n\t\t\t\t).data.map((id) => id.toLowerCase());\r\n\r\n\t\t\t\tconst validOutsideTimeSpan: RecentlyViewedProduct[] = outsideTimeSpan.filter((product) =>\r\n\t\t\t\t\tvalidProductIds.includes(product.id.toLowerCase()),\r\n\t\t\t\t);\r\n\r\n\t\t\t\tfor (const product of validOutsideTimeSpan) {\r\n\t\t\t\t\tproduct.lastViewed = now;\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn withinTimeSpan.concat(validOutsideTimeSpan);\r\n\t\t\t}\r\n\t\t} catch (error) {\r\n\t\t\tconsole.error(`An error occurred while filtering out invalid products: ${error.message}`);\r\n\t\t}\r\n\r\n\t\treturn products;\r\n\t}\r\n\r\n\t/**\r\n\t * Update recently viewed products state in the local storage\r\n\t */\r\n\tpublic setAll(products: RecentlyViewedProduct[]) {\r\n\t\tconst context: RecentlyViewedProductsContext = {\r\n\t\t\tproducts,\r\n\t\t\ttimestamp: new Date().valueOf(),\r\n\t\t};\r\n\t\tlocalStorage.setItem(this.keyName, JSON.stringify(context));\r\n\t}\r\n}\r\n\r\nconst repository = new RecentlyViewedProductsRepository();\r\n\r\n/**\r\n * Custom hook for recently viewed products\r\n */\r\nexport default function useRecentlyViewedProducts({\r\n\tcurrentProduct,\r\n\tactive = true,\r\n}: UseRecentlyViewedProductsParams): RecentlyViewedProduct[] {\r\n\tconst maxPersistedProducts = 5;\r\n\tconst [recentlyViewedProducts, setRecentlyViewedProducts] = useState([]);\r\n\r\n\t/** Retrieve the partial product state for an arbitrary product */\r\n\tasync function getProductStateAsync(productId: string) {\r\n\t\ttry {\r\n\t\t\tconst productState = await getPartialProductData({\r\n\t\t\t\tid: productId,\r\n\t\t\t\tmissingKeys: sparseProductDetailsStateKeys,\r\n\t\t\t});\r\n\t\t\tconsole.debug('Product state retrieved:', productState);\r\n\r\n\t\t\tif (productState != null) {\r\n\t\t\t\tconst recentlyViewedProduct: RecentlyViewedProduct = {\r\n\t\t\t\t\t...productState,\r\n\t\t\t\t\tid: productId,\r\n\t\t\t\t\tlastViewed: new Date().valueOf(),\r\n\t\t\t\t};\r\n\r\n\t\t\t\treturn recentlyViewedProduct;\r\n\t\t\t}\r\n\t\t} catch (error) {\r\n\t\t\tconsole.error(error);\r\n\t\t}\r\n\r\n\t\tconsole.error('Failed to retrieve the product with the given id:', productId);\r\n\t\treturn null;\r\n\t}\r\n\r\n\tuseEffect(() => {\r\n\t\tif (!active) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\t(async () => {\r\n\t\t\tlet recentlyViewedProductDtos = await repository.getAllValidOnly();\r\n\r\n\t\t\t// process current product only when it was fully loaded w/o any errors\r\n\t\t\tif (currentProduct != null) {\r\n\t\t\t\tconst currentProductDto: RecentlyViewedProduct = {\r\n\t\t\t\t\tid: currentProduct.id,\r\n\t\t\t\t\tsummary: currentProduct.summary,\r\n\t\t\t\t\tassets: currentProduct.assets,\r\n\t\t\t\t\tpricingLines: currentProduct.pricingLines,\r\n\t\t\t\t\tdisplayPricing: currentProduct.displayPricing,\r\n\t\t\t\t\tlastViewed: new Date().valueOf(),\r\n\t\t\t\t};\r\n\r\n\t\t\t\t// If the product is already in the recently review list, remove it because it will be reinserted at the end of this method in order to refresh it with up to date data.\r\n\t\t\t\tconst currentProductIndex = recentlyViewedProductDtos.findIndex(\r\n\t\t\t\t\t(item) => item.id === currentProductDto.id,\r\n\t\t\t\t);\r\n\t\t\t\tif (currentProductIndex > -1) {\r\n\t\t\t\t\trecentlyViewedProductDtos.splice(currentProductIndex, 1);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// If the product doesn't have a parent, add it to recently viewed.\r\n\t\t\t\t// If the user ended up following a direct link to a simple with a parent, it will not be added to recently viewed despite this being undesirable.\r\n\t\t\t\tif (currentProductDto.summary != null) {\r\n\t\t\t\t\tconst groupId = currentProductDto.summary.productGroupId;\r\n\t\t\t\t\tif (groupId == null) {\r\n\t\t\t\t\t\trecentlyViewedProductDtos.unshift(currentProductDto); // insert current item at the first place\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tconst groupIndex = recentlyViewedProductDtos.findIndex((item) => item.id === groupId);\r\n\t\t\t\t\t\tif (groupIndex > 0) {\r\n\t\t\t\t\t\t\t// Move the group to the front of the list but don't reload it.\r\n\t\t\t\t\t\t\tconst groupProducts = recentlyViewedProductDtos.splice(groupIndex, 1);\r\n\t\t\t\t\t\t\trecentlyViewedProductDtos.unshift(groupProducts[0]);\r\n\t\t\t\t\t\t} else if (groupIndex === -1) {\r\n\t\t\t\t\t\t\t// Attempt to retrieve enough product state information to insert a recently viewed product entry for the group.\r\n\t\t\t\t\t\t\tconst currentGroupDto = await getProductStateAsync(groupId);\r\n\t\t\t\t\t\t\tif (currentGroupDto != null) {\r\n\t\t\t\t\t\t\t\trecentlyViewedProductDtos.unshift(currentGroupDto);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\trecentlyViewedProductDtos = recentlyViewedProductDtos.slice(0, maxPersistedProducts);\r\n\t\t\t\trepository.setAll(recentlyViewedProductDtos);\r\n\t\t\t\trecentlyViewedProductDtos = recentlyViewedProductDtos.filter(\r\n\t\t\t\t\t(p) => p.id !== currentProductDto.id && p.id !== currentProductDto.summary?.productGroupId,\r\n\t\t\t\t); // exclude current product and/or group from being rendered\r\n\t\t\t}\r\n\r\n\t\t\tsetRecentlyViewedProducts(recentlyViewedProductDtos);\r\n\t\t})();\r\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\r\n\t}, [currentProduct && currentProduct.id, active]);\r\n\r\n\treturn recentlyViewedProducts;\r\n}\r\n","import debounce from 'lodash-es/debounce';\r\nimport React, { useEffect, useRef } from 'react';\r\nimport { Localizer } from 'services/Localizer';\r\nimport Spinner from 'shared/components/Spinner';\r\nimport { useInViewOnce } from 'shared/hooks/useInViewOnce';\r\nimport type { TinySliderInstance, TinySliderSettings } from 'tiny-slider/src/tiny-slider';\r\nimport uuid from 'uuid/v4';\r\nimport { getInitialProductDetailsState } from '../product-details/types';\r\nimport RecentlyViewedProduct from './RecentlyViewedProduct';\r\nimport useRecentlyViewedProducts from './useRecentlyViewedProducts';\r\nimport { sm, md, lg } from 'shared/breakpoints';\r\n\r\ninterface RecentlyViewedProductsProps {\r\n\t/** If this originated from a tab, this will be the ID of that tab */\r\n\ttabId?: string;\r\n\tdisplaytitle?: boolean;\r\n}\r\n\r\n/**\r\n * Represents a listing of recently viewed products for a user, found as a \"tab\" under the product details page.\r\n * This is not a standard react component and relies on global state to function properly.\r\n */\r\nexport default function RecentlyViewedProducts(props: RecentlyViewedProductsProps) {\r\n\tconst productDetailsState = getInitialProductDetailsState();\r\n\tconst recentlyViewedProducts = useRecentlyViewedProducts({ currentProduct: productDetailsState, active: true });\r\n\tconst currentInstanceId = useRef('recently-viewed-products-slider-' + uuid()).current;\r\n\tconst sliderRef = useRef(null);\r\n\r\n\tconst { displaytitle = false } = props;\r\n\r\n\tconst [ref, inView] = useInViewOnce({\r\n\t\tthreshold: 0,\r\n\t});\r\n\r\n\tconst tnsPromise = import('tiny-slider/src/tiny-slider').then((module) => module.tns);\r\n\tconst oneItemClass = 'roc-product-list--one-item';\r\n\tconst twoItemClass = 'roc-product-list--two-items';\r\n\tconst threeItemClass = 'roc-product-list--three-items';\r\n\tconst fiveItemClass = 'roc-product-list--five-items';\r\n\r\n\t// Handle debouncing the recreation of tiny-slider on resize\r\n\tconst handleResizeDebounced = () => {\r\n\t\tinitializeSlider();\r\n\t};\r\n\r\n\tconst executeResizeDebounced = debounce(handleResizeDebounced, 200);\r\n\r\n\t// bind tiny-slider on startup or resize\r\n\tconst initializeSlider = () => {\r\n\t\tif (sliderRef.current) {\r\n\t\t\tsliderRef.current.destroy();\r\n\t\t}\r\n\r\n\t\t(async () => {\r\n\t\t\tconst currentSliderDiv = document.getElementById(currentInstanceId);\r\n\t\t\tconst tns = await tnsPromise;\r\n\r\n\t\t\tconst getTinySliderInstance = (props: { itemsToShow: number }) => {\r\n\t\t\t\tconst options: TinySliderSettings = {\r\n\t\t\t\t\tcontainer: `#${currentInstanceId}`,\r\n\t\t\t\t\titems: props.itemsToShow,\r\n\t\t\t\t\tslideBy: 'page',\r\n\t\t\t\t\tcontrols: false,\r\n\t\t\t\t\tmouseDrag: true,\r\n\t\t\t\t\tgutter: 15,\r\n\t\t\t\t\tlazyload: true,\r\n\t\t\t\t\tloop: false,\r\n\t\t\t\t\tedgePadding: 88,\r\n\t\t\t\t};\r\n\r\n\t\t\t\treturn tns(options);\r\n\t\t\t};\r\n\r\n\t\t\tif (currentSliderDiv) {\r\n\t\t\t\tconst trackElement = currentSliderDiv.parentElement;\r\n\t\t\t\tconst parentRocProductList = currentSliderDiv.closest('.roc-product-list');\r\n\t\t\t\tif (trackElement && parentRocProductList) {\r\n\t\t\t\t\tconst containerWidth = trackElement.offsetWidth;\r\n\t\t\t\t\t// Different tiny-slider settings based on parent element width.\r\n\t\t\t\t\t// The \"responsive\" option on tiny slider is based on window width, not container element width, so we can't use it in this case\r\n\t\t\t\t\tif (containerWidth < sm) {\r\n\t\t\t\t\t\t// 1 item\r\n\t\t\t\t\t\tparentRocProductList.classList.remove(twoItemClass, threeItemClass, fiveItemClass);\r\n\t\t\t\t\t\tparentRocProductList.classList.add(oneItemClass);\r\n\r\n\t\t\t\t\t\tsliderRef.current = getTinySliderInstance({ itemsToShow: 1 });\r\n\t\t\t\t\t} else if (containerWidth < md) {\r\n\t\t\t\t\t\t// 2 items\r\n\t\t\t\t\t\tparentRocProductList.classList.remove(oneItemClass, threeItemClass, fiveItemClass);\r\n\t\t\t\t\t\tparentRocProductList.classList.add(twoItemClass);\r\n\r\n\t\t\t\t\t\tsliderRef.current = getTinySliderInstance({ itemsToShow: 2 });\r\n\t\t\t\t\t} else if (containerWidth < lg) {\r\n\t\t\t\t\t\t// 3 items\r\n\t\t\t\t\t\tparentRocProductList.classList.remove(oneItemClass, twoItemClass, fiveItemClass);\r\n\t\t\t\t\t\tparentRocProductList.classList.add(threeItemClass);\r\n\r\n\t\t\t\t\t\tsliderRef.current = getTinySliderInstance({ itemsToShow: 3 });\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\t// 5 items\r\n\t\t\t\t\t\tparentRocProductList.classList.remove(oneItemClass, twoItemClass, threeItemClass);\r\n\t\t\t\t\t\tparentRocProductList.classList.add(fiveItemClass);\r\n\r\n\t\t\t\t\t\tsliderRef.current = getTinySliderInstance({ itemsToShow: 5 });\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// if there's a re-render, or possibly hmr, make sure to destroy the instance\r\n\t\t\t// to avoid memory leaks.\r\n\t\t\treturn () => {\r\n\t\t\t\tif (sliderRef.current) {\r\n\t\t\t\t\tsliderRef.current.destroy();\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t\t})();\r\n\t};\r\n\r\n\t// Bind tiny-slider on startup\r\n\tuseEffect(() => {\r\n\t\tinitializeSlider();\r\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\r\n\t}, [inView, recentlyViewedProducts]);\r\n\r\n\t// Set up an event handler to re-create the tiny slider when the window is resized\r\n\tuseEffect(() => {\r\n\t\tconst handleResize = () => {\r\n\t\t\texecuteResizeDebounced();\r\n\t\t};\r\n\r\n\t\twindow.addEventListener('resize', handleResize);\r\n\r\n\t\treturn () => {\r\n\t\t\twindow.removeEventListener('resize', handleResize);\r\n\t\t};\r\n\t}, [executeResizeDebounced]);\r\n\r\n\tif (!recentlyViewedProducts || recentlyViewedProducts.length === 0 || !inView) {\r\n\t\treturn
;\r\n\t}\r\n\r\n\treturn (\r\n\t\t}>\r\n\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t\t\t{displaytitle ? (\r\n\t\t\t\t\t\t

\r\n\t\t\t\t\t\t\t{Localizer('RecentlyViewedProducts')}\r\n\t\t\t\t\t\t

\r\n\t\t\t\t\t) : null}\r\n\r\n\t\t\t\t\t
\r\n\t\t\t\t\t\t{recentlyViewedProducts.map((item, i) => (\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t))}\r\n\t\t\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t
\r\n\t\t
\r\n\t);\r\n}\r\n","import React from 'react';\r\nimport classnames from 'classnames';\r\nimport { Localizer } from 'services/Localizer';\r\nimport { AxiosError } from 'axios';\r\nimport XSVG from 'shared/components/svg/XSVG';\r\n\r\ntype Theme = 'danger' | 'warning' | 'info' | 'success';\r\n\r\nexport interface ErrorAlertProps {\r\n\t/**\r\n\t * The title of the error message such as \"Uh Oh!\" or \"An Unexpected Error has Occurred!\"\r\n\t */\r\n\ttitle: string;\r\n\r\n\t/**\r\n\t * The error message to display if an error object doesn't have a more appropriate message.\r\n\t */\r\n\tmessage: React.ReactNode | string;\r\n\r\n\tchildren?: any;\r\n\r\n\ttheme?: Theme;\r\n\r\n\tisTitleOnOwnRow?: boolean;\r\n\r\n\t/** The error responsible for this message. */\r\n\terror: AxiosError | Error | null;\r\n\r\n\t/**\r\n\t * Method from parent that handles collapse state.\r\n\t */\r\n\tonCollapse?: (collased: boolean) => void;\r\n\r\n\t/**\r\n\t * Controlled collapse state from parent.\r\n\t */\r\n\tisCollapsed?: boolean;\r\n}\r\n\r\n/**\r\n * Reusable Error Alert Component.\r\n */\r\nexport default function InlineAlert(props: ErrorAlertProps) {\r\n\tconst {\r\n\t\ttitle = InlineAlert.defaultProps.title,\r\n\t\tchildren,\r\n\t\ttheme = 'danger',\r\n\t\tisTitleOnOwnRow = false,\r\n\t\terror = InlineAlert.defaultProps.error,\r\n\t\tisCollapsed = false,\r\n\t\tonCollapse,\r\n\t} = props;\r\n\r\n\tlet { message = InlineAlert.defaultProps.message } = props;\r\n\r\n\tconst axiosError = error as AxiosError | null;\r\n\tif (axiosError?.isAxiosError && axiosError != null && axiosError.response == null && axiosError.request != null) {\r\n\t\tmessage = Localizer('ErrorMessageNetwork');\r\n\t}\r\n\r\n\tif (axiosError?.isAxiosError && (axiosError?.response?.status === 401 || axiosError?.response?.status === 403)) {\r\n\t\tmessage = Localizer('ErrorMessageForbidden');\r\n\t}\r\n\r\n\tif (axiosError?.response?.data?.message) {\r\n\t\tmessage = axiosError.response.data.message;\r\n\t}\r\n\r\n\tif (isCollapsed) {\r\n\t\treturn null;\r\n\t}\r\n\r\n\treturn (\r\n\t\t\r\n\t\t\t{onCollapse ? (\r\n\t\t\t\t\r\n\t\t\t) : null}\r\n\r\n\t\t\t{title ? (\r\n\t\t\t\t\r\n\t\t\t\t\t{title}\r\n\t\t\t\t\r\n\t\t\t) : null}\r\n\r\n\t\t\t{message ? {message} : null}\r\n\r\n\t\t\t{children ? <> {children} : null}\r\n\t\t
\r\n\t);\r\n}\r\n\r\n/**\r\n * Error Alert Default Props\r\n */\r\nInlineAlert.defaultProps = {\r\n\ttitle: Localizer('ErrorMessageGenericTitle'),\r\n\tmessage: Localizer('UnexpectedErrorOccurred'),\r\n\terror: null,\r\n};\r\n","import { useEffect } from 'react';\r\nimport 'shared/polyfills/customEvent';\r\nimport { RocEventType, ExtractEventParameters, RocEvent } from 'shared/events';\r\nimport addRocEventHandler from 'shared/util/events/rocEventUtils';\r\n\r\n/**\r\n * Helper function for dispatching ROC events.\r\n *\r\n * @param eventType The event type for the dispatcher\r\n */\r\nexport function dispatchRocEvent(eventType: T, data: ExtractEventParameters) {\r\n\tconst event = new CustomEvent(eventType, {\r\n\t\tdetail: data,\r\n\t});\r\n\tdocument.dispatchEvent(event);\r\n}\r\n\r\n/**\r\n * Reusable hook for handling ROC events\r\n *\r\n * @param eventType The type of event to handle\r\n * @param eventHandler The event handler\r\n * @param deps List of dependencies\r\n */\r\nexport default function useRocEventHandler(\r\n\teventType: T,\r\n\teventHandler: (data: ExtractEventParameters) => void,\r\n\tdeps?: any[],\r\n) {\r\n\tuseEffect(() => {\r\n\t\tconst removeHandlerCallback = addRocEventHandler(eventType, eventHandler);\r\n\r\n\t\treturn () => {\r\n\t\t\tremoveHandlerCallback();\r\n\t\t};\r\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\r\n\t}, deps);\r\n}\r\n","import React from 'react';\r\nimport { SVGProps } from './SVGProps';\r\nimport classnames from 'classnames';\r\n\r\n/**\r\n * X SVG\r\n *\r\n * @returns\r\n */\r\nfunction XSVG({ className, ...rest }: SVGProps) {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\r\n\t);\r\n}\r\n\r\nexport default XSVG;\r\n","import AxiosHelper from 'services/AxiosHelper';\r\nimport { AxiosResponse } from 'axios';\r\n\r\nexport interface UserSummary {\r\n\tisSignedIn: boolean;\r\n\tuserName: string | null;\r\n\tfirstName: string | null;\r\n\tlastName: string | null;\r\n\temail: string | null;\r\n\tphoneNumber: string | null;\r\n\ttwoFactorEnabled: boolean;\r\n\thasRecoveryCodes: boolean;\r\n\tisMachineRemembered: boolean;\r\n\tannouncements: UserAnnouncement[];\r\n\tisExternalLoginUser: boolean;\r\n\t// #region HCL\r\n\tversaPayEnabled: boolean;\r\n\tversaPayRedirectUrl: string | null;\r\n\tazureUserId: string | null;\r\n\t// #endregion\r\n}\r\n\r\n/** Represents the details about an admin impersonation session of the logged in user. */\r\nexport interface Impersonation {\r\n\t/** The id of the admin impersonating the front end user. */\r\n\timpersonatorId: string;\r\n\r\n\t/** The name of the admin impersonating the front end user. */\r\n\timpersonatorName: string;\r\n\r\n\t/** The id of the active user impersonation session. */\r\n\tsessionId: string;\r\n}\r\n\r\nexport interface UserData {\r\n\t/**\r\n\t * Summary of user-specific details\r\n\t */\r\n\tsummary?: UserSummary;\r\n\r\n\t/**\r\n\t * CSRF validation token generated by the server\r\n\t */\r\n\trequestToken?: string;\r\n\r\n\t/**\r\n\t * The information on an impersonation session if one is active.\r\n\t * If an impersonation session isn't active this field will be undefined.\r\n\t */\r\n\timpersonation?: Impersonation;\r\n}\r\n\r\nexport interface UserAnnouncement {\r\n\t/** The id used as the primary key for the announcement in the database */\r\n\tid: string;\r\n\r\n\t/** An announcement to be displayed to the user */\r\n\tmessage: string;\r\n\r\n\t/** A list of pages where the announcement will be displayed. This will be an empty array if it is to be displayed on all pages. */\r\n\tpages: string[];\r\n}\r\n\r\nlet userDataPromise: Promise> | null = null;\r\n\r\n/**\r\n * Returns details related to the current user. By default, this is primarily their first/last name\r\n * and whether they are logged in or not. This can customized to include more details if necessary.\r\n */\r\nexport const getUserData = () => {\r\n\tif (userDataPromise === null) {\r\n\t\tuserDataPromise = AxiosHelper.get('/ajax/user');\r\n\t}\r\n\r\n\treturn userDataPromise;\r\n};\r\n\r\n/**\r\n * Returns non-cached details related to the current user. By default, this is primarily their first/last name\r\n * and whether they are logged in or not. This can customized to include more details if necessary.\r\n */\r\nexport const getUserDataNoCache = () => {\r\n\treturn AxiosHelper.get('/ajax/user');\r\n};\r\n\r\n/**\r\n * Returns a promise that will contain the CSRF token generated on the server to be used to validate\r\n * forms being posted via AJAX.\r\n */\r\nexport async function getCsrfToken() {\r\n\ttry {\r\n\t\tconst userData = await getUserData();\r\n\t\treturn userData.data.requestToken;\r\n\t} catch (error) {\r\n\t\tconsole.warn('Unable to load CSRF token.');\r\n\t\treturn '';\r\n\t}\r\n}\r\n\r\n// #region Commerce\r\nimport { CartSummary } from './UserData.Commerce';\r\nimport { LocationDto } from 'features/commerce/store-locator/types';\r\nimport { OrganizationInvite, OrganizationShipTo } from 'shared-client/types/commerce/organization';\r\nimport { AutoSuggestOption } from '../../../Roc.SharedClientApp/types/fields';\r\n\r\n// Commerce extensions to the UserData interface\r\nexport interface UserData {\r\n\t// #region HCL - allows null\r\n\tcartSummary?: CartSummary | null;\r\n\t// #endregion\r\n\r\n\t/***\r\n\t * The customer's default store location or null if not set\r\n\t */\r\n\tdefaultStoreLocation?: LocationDto | null;\r\n\r\n\t/**\r\n\t * If store is enabled for this site\r\n\t */\r\n\tenableStoreLocation?: boolean;\r\n}\r\n\r\n/***\r\n * Organization data and permissions\r\n */\r\nexport interface Organization {\r\n\tid: string;\r\n\tpurchaseOrderNumberRequired: boolean;\r\n\tcreditCardAllowed: boolean;\r\n\tgiftCardAllowed: boolean;\r\n\torganizationName: string;\r\n\tcorpAccountAllowed: boolean;\r\n}\r\n\r\n/***\r\n * Organization dto data\r\n */\r\nexport interface OrganizationDto extends AutoSuggestOption {}\r\n\r\n/***\r\n * User's organization and permissions data\r\n */\r\nexport interface UserSummary {\r\n\t/***\r\n\t * User's current default organization\r\n\t */\r\n\torganization: Organization | null;\r\n\r\n\t/***\r\n\t * The list of organizations the user is part of\r\n\t */\r\n\tuserOrganizations?: OrganizationDto[] | null;\r\n\r\n\t/***\r\n\t * The selected organization ship to\r\n\t */\r\n\tselectedShipTo: OrganizationShipTo | null;\r\n\r\n\t/***\r\n\t * Defines if the user has multiple shiptos to select from\r\n\t */\r\n\thasMultipleShipTos: boolean | null;\r\n\r\n\t/***\r\n\t * Defines if the user has saved payment methods\r\n\t */\r\n\thasSavedPaymentMethods: boolean | null;\r\n\r\n\t/***\r\n\t * The list of user permissions\r\n\t */\r\n\tpermissions?: UserPermissions;\r\n\r\n\t/***\r\n\t * The list of organizations the user is invited to join\r\n\t */\r\n\torganizationInvites?: OrganizationInvite[];\r\n\r\n\t/***\r\n\t * The user organization removal notification\r\n\t * Used to notify the user if he is no more part of his default organization\r\n\t */\r\n\tuserOrganizationRemovalNotification?: string;\r\n}\r\n\r\n/***\r\n * User's permissions\r\n */\r\nexport interface UserPermissions {\r\n\t/***\r\n\t * Defines if the current user can see organization users\r\n\t */\r\n\tcanSeeOrganizationUsers?: boolean;\r\n\r\n\t/***\r\n\t * Defines if the current user can assign roles to organization users\r\n\t */\r\n\tcanAssignRolesToOrganizationUsers?: boolean;\r\n\r\n\t/***\r\n\t * Defines if the current user can remove users from an organization\r\n\t */\r\n\tremoveUserFromOrganization?: boolean;\r\n\r\n\t/***\r\n\t * Defines if the current user can create organization users\r\n\t */\r\n\tcreateOrganizationUser?: boolean;\r\n\r\n\t/***\r\n\t * Defines if the current user can enable/disable ShipTos for organization users\r\n\t */\r\n\tmanageShipTosAssociation?: boolean;\r\n\r\n\t/***\r\n\t * Defines if the current user can see status of the organization users\r\n\t */\r\n\tcanSeePendingOrganizationUsers?: boolean;\r\n}\r\n// #endregion\r\n","import React from 'react';\r\nimport InlineAlert from 'shared/edit/InlineAlert';\r\n\r\n// eslint-disable-next-line\r\n// tslint:disable-next-line\r\nexport interface ErrorBoundaryRenderProps extends ErrorBoundaryState {}\r\n\r\nexport interface ErrorBoundaryProps {\r\n\tchildren: React.ReactNode;\r\n\trenderError?: (renderProps: ErrorBoundaryRenderProps) => React.ReactNode;\r\n}\r\n\r\nexport interface ErrorBoundaryState {\r\n\terror?: Error;\r\n}\r\n\r\n/**\r\n * Default Error Boundary component for frontend components, so that in case a JS error occurs, we show\r\n * something instead of nothing.\r\n */\r\nexport default class ErrorBoundary extends React.Component {\r\n\tpublic static defaultProps = {\r\n\t\trenderError: (renderProps: ErrorBoundaryRenderProps) => ,\r\n\t};\r\n\r\n\tpublic state: ErrorBoundaryState = {};\r\n\r\n\tpublic componentDidCatch(error: Error) {\r\n\t\tif (error.name === 'ChunkLoadError') {\r\n\t\t\tconst lastRefreshedTimestamp = new Date(\r\n\t\t\t\tparseInt(window.localStorage.getItem('force-refresh-timestamp') || '0'),\r\n\t\t\t);\r\n\t\t\tconst pageAlreadyRefreshedLessThan30SecondsAgo =\r\n\t\t\t\tnew Date(lastRefreshedTimestamp.getTime() + 30 * 1000) > new Date();\r\n\r\n\t\t\tif (!pageAlreadyRefreshedLessThan30SecondsAgo) {\r\n\t\t\t\t// Assuming that the user is not on the latest version of the application. Refresh the page immediately.\r\n\t\t\t\twindow.localStorage.setItem('force-refresh-timestamp', new Date().getTime().toString());\r\n\t\t\t\treturn window.location.reload();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis.setState({\r\n\t\t\terror,\r\n\t\t});\r\n\t}\r\n\r\n\tpublic render() {\r\n\t\tif (this.state.error) {\r\n\t\t\treturn this.props.renderError!(this.state);\r\n\t\t}\r\n\r\n\t\treturn this.props.children;\r\n\t}\r\n}\r\n","import { ServerData } from 'services/ServerData';\r\n\r\nexport default {\r\n\tBaseUrl: ServerData.ACCOUNT_URL_BASE ?? '/my-account',\r\n\tLogin: ServerData.ACCOUNT_LOGIN_URL ?? '/my-account/login',\r\n\tLogin2fa: ServerData.ACCOUNT_LOGIN_WITH_2FA_URL ?? '/my-account/login-with-2fa',\r\n\tRegister: ServerData.ACCOUNT_REGISTER_URL ?? '/my-account/register',\r\n\tForgotPassword: ServerData.ACCOUNT_FORGOT_PASSWORD_URL ?? '/my-account/forgot-password',\r\n\tLoginWithRecoveryCode: ServerData.ACCOUNT_LOGIN_WITH_RECOVERY_CODE_URL ?? '/my-account/login-with-recovery-code',\r\n\tLogout: ServerData.ACCOUNT_LOGOUT_URL ?? '/my-account/logout',\r\n\t// #region Commerce\r\n\tOrganizationReview: ServerData.ACCOUNT_ORGANIZATION_REVIEW_URL ?? '/my-account/organization-review',\r\n\tOrganizationSelector: ServerData.ACCOUNT_ORGANIZATION_SELECTOR_URL ?? '/my-account/organization-selector',\r\n\t// #endregion\r\n\t// #region HCL - hardcoded same way as external login is\r\n\tExternalLogout: '/my-account/external-logout',\r\n\t// #endregion\r\n};\r\n","// source: https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttributeNames\r\n/* eslint-disable no-var */\r\n(function () {\r\n\tif (Element.prototype.getAttributeNames == undefined) {\r\n\t\tElement.prototype.getAttributeNames = function () {\r\n\t\t\tvar attributes = this.attributes;\r\n\t\t\tvar length = attributes.length;\r\n\t\t\tvar result = new Array(length);\r\n\t\t\tfor (var i = 0; i < length; i++) {\r\n\t\t\t\tresult[i] = attributes[i].name;\r\n\t\t\t}\r\n\t\t\treturn result;\r\n\t\t};\r\n\t}\r\n})();\r\n","import React from 'react';\r\nimport ReactDOM from 'react-dom';\r\nimport ErrorBoundary from './ErrorBoundary';\r\nimport { toCamelCase } from 'util/StringUtil';\r\nimport 'shared/polyfills/getAttributeNames';\r\nimport Spinner from 'shared/components/Spinner';\r\n\r\ninterface AttributeProps {\r\n\t[index: string]: string;\r\n}\r\n\r\nfunction getDataPropsFromAttributes(element: Element): AttributeProps {\r\n\tconst props: AttributeProps = {};\r\n\r\n\tconst dataAttributePrefix = 'data-';\r\n\r\n\tconst attributeNames = element.getAttributeNames().filter((s) => s.startsWith(dataAttributePrefix));\r\n\tfor (const attributeName of attributeNames) {\r\n\t\tconst strippedName = toCamelCase(attributeName.substring(dataAttributePrefix.length));\r\n\t\tprops[strippedName] = element.getAttribute(attributeName) ?? '';\r\n\t}\r\n\r\n\treturn props;\r\n}\r\n\r\n/**\r\n * A helper function to facilitate initializing react applications on top of server-side rendered elements.\r\n * @param getReactElement Function that returns the react component to render.\r\n * @param elementId The ID of the DOM element being targeted.\r\n */\r\nexport const initReactApp = (getReactElement: (props: unknown) => React.ReactElement, elementId: string) => {\r\n\tif (typeof getReactElement !== 'function' || !elementId) {\r\n\t\treturn () => {\r\n\t\t\tconsole.warn('A react element generator function and element ID are required.');\r\n\t\t};\r\n\t}\r\n\r\n\tconst element = document.getElementById(elementId);\r\n\r\n\tif (!element) {\r\n\t\treturn () => {\r\n\t\t\tconsole.warn(\r\n\t\t\t\t`Element with ID '${elementId}' was not found. Did you forget to render it on the server or is this entry point not supposed to run?`,\r\n\t\t\t);\r\n\t\t};\r\n\t}\r\n\r\n\treturn () => {\r\n\t\tconst props = getDataPropsFromAttributes(element);\r\n\t\tconsole.debug(`Rendering application inside element with ID ${elementId}.`);\r\n\t\tReactDOM.render(\r\n\t\t\t}>\r\n\t\t\t\t{getReactElement(props)}\r\n\t\t\t,\r\n\t\t\telement,\r\n\t\t);\r\n\t};\r\n};\r\n\r\nexport const initReactAppByAttribute = (\r\n\tgetReactElement: (props: unknown, domElement: Element) => React.ReactElement,\r\n\tattribute: string,\r\n) => {\r\n\tif (typeof getReactElement !== 'function' || !attribute) {\r\n\t\treturn () => {\r\n\t\t\tconsole.warn('A react element generator function and element ID are required.');\r\n\t\t};\r\n\t}\r\n\r\n\tconst elements = document.querySelectorAll(`[${attribute}]`);\r\n\r\n\tif (!elements || elements.length == 0) {\r\n\t\treturn () => {\r\n\t\t\tconsole.warn(\r\n\t\t\t\t`No elements with data attribute '${attribute}' were found. Did you forget to render it on the server or is this entry point not supposed to run?`,\r\n\t\t\t);\r\n\t\t};\r\n\t}\r\n\r\n\treturn () => {\r\n\t\tfor (let i = 0; i < elements.length; ++i) {\r\n\t\t\tconst element = elements[i];\r\n\t\t\tconst props = getDataPropsFromAttributes(element);\r\n\t\t\tconsole.debug(`Rendering application inside element with attribute ${attribute}.`);\r\n\t\t\tReactDOM.render(\r\n\t\t\t\t}>\r\n\t\t\t\t\t{getReactElement(props, element)}\r\n\t\t\t\t,\r\n\t\t\t\telement,\r\n\t\t\t);\r\n\t\t}\r\n\t};\r\n};\r\n","/**\r\n * THIS HAS A COPY ON ROC.WEB. DO NOT MAKE CHANGES HERE WITHOUT ALSO MAKING THEM IN THE OTHER FILE.\r\n *\r\n * THIS MUST ALSO BE KEPT IN SYNC WITH THE \"RazorDisplayPrice\" VIEW COMPONENT.\r\n *\r\n * DIVERGENCES:\r\n * - The style \"visually-hidden\" DOES NOT EXIST ON ROC.WEB. Change this to \"roc-visually-hidden\"\r\n */\r\n\r\nimport classnames from 'classnames';\r\nimport React from 'react';\r\nimport { Localizer } from 'services/Localizer';\r\nimport { CssClasses, ProductPricingProps } from 'shared-client/types/commerce/product-details';\r\nimport {\r\n\tAmbiguousDisplayPrice,\r\n\tMissingDisplayPrice,\r\n\tPartialDisplayPrice,\r\n\tPrice,\r\n\tPriceDisplayRule,\r\n\tPriceSpan,\r\n\tSimpleDisplayPrice,\r\n} from 'shared-client/types/commerce/display-price';\r\n\r\nimport { PricingResourceKeys } from 'features/commerce/pricing/resources';\r\n\r\nconst GetCssClasses = (className: string | undefined, inline: boolean) => {\r\n\tconst priceClass = ['roc-pricing__price', className ? className + '__price' : null];\r\n\r\n\treturn {\r\n\t\tbase: ['roc-pricing', { 'roc-pricing--inline': inline }, className ? className : null],\r\n\t\tnormalPrice: ['roc-pricing__price', className ? className + '__price' : null],\r\n\t\tcrossedOutPrice: [\r\n\t\t\tpriceClass,\r\n\t\t\t'roc-pricing__price--original',\r\n\t\t\tclassName ? className + '__price--original' : null,\r\n\t\t],\r\n\t\thighlightedPrice: [priceClass, 'roc-pricing__price--sale', className ? className + '__price--sale' : null],\r\n\t};\r\n};\r\n\r\n/**\r\n * Renders a product price. This could be a simple price \"$4.99\", a price that's on sale \"~~$4.99~~ $3.99\",\r\n * a range of prices, an ambiguous price or potentially others.\r\n *\r\n * The pricing information is expected to have come from the server where it has already been validated.\r\n * It is the responsibility of the caller to verify pricing objects passed to ProductPricing are valid.\r\n */\r\nexport default function ProductPricing(props: ProductPricingProps) {\r\n\t// #region HCL\r\n\tconst { displayPrice, className, inline = false, testId = 'product-price', roundValue } = props;\r\n\t// #endregion\r\n\r\n\t// The server/caller is trusted to have provided valid props.\r\n\t// Do no validation here.\r\n\r\n\tif (displayPrice == null) {\r\n\t\t// Missing price. Remove this if MissingPrice functionality is ever needed beyond \"don't render\"\r\n\t\treturn null;\r\n\t}\r\n\r\n\tconst css = GetCssClasses(className, inline);\r\n\r\n\tlet renderResult: JSX.Element | null;\r\n\tswitch (displayPrice.displayRule) {\r\n\t\tcase PriceDisplayRule.Missing:\r\n\t\t\trenderResult = renderMissingPrice(css, displayPrice);\r\n\t\t\tbreak;\r\n\r\n\t\tcase PriceDisplayRule.Normal:\r\n\t\t\trenderResult = renderNormalPrice(css, displayPrice, testId);\r\n\t\t\tbreak;\r\n\r\n\t\tcase PriceDisplayRule.AmbiguousRange:\r\n\t\t\t// #region HCL\r\n\t\t\trenderResult = renderAmbiguousPrice(css, displayPrice, testId, roundValue);\r\n\t\t\t// #endregion\r\n\t\t\tbreak;\r\n\r\n\t\tdefault:\r\n\t\t\tthrow new Error('Erroneous price display rule');\r\n\t}\r\n\r\n\treturn (\r\n\t\t\r\n\t\t\t{renderResult}\r\n\t\t\r\n\t);\r\n}\r\n\r\n/**\r\n * Renders a missing price, eg nothing. This currently renders nothing but could be updated to render \"Not For Sale\" for example.\r\n * @param css The set of css rules that can be applied.\r\n * @param price The price object\r\n */\r\n// eslint-disable-next-line no-unused-vars\r\nfunction renderMissingPrice(css: CssClasses, price: MissingDisplayPrice) {\r\n\treturn null;\r\n}\r\n\r\n/**\r\n * Renders a normal price. This is a price that might be on sale and may either be a unit price (eg $4.99) or a\r\n * range (eg $3.99 - $7.99).\r\n * @param css The set of css rules that can be applied.\r\n * @param price The price object\r\n */\r\nfunction renderNormalPrice(css: CssClasses, price: SimpleDisplayPrice, testId: string) {\r\n\t// conditionally cross out the base price\r\n\tconst basePriceClass = price.salePrice != null ? css.crossedOutPrice : css.normalPrice;\r\n\r\n\treturn (\r\n\t\t<>\r\n\t\t\t\r\n\t\t\t\t{Localizer('OriginalPrice')}\r\n\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\t{price.salePrice != null ? (\r\n\t\t\t\t\r\n\t\t\t\t\t{Localizer('SalePrice')}\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t) : null}\r\n\t\t\r\n\t);\r\n}\r\n\r\n/**\r\n * Renders an ambiguous price. This is a compound price representing multiple different products where the upperbound of the price\r\n * can't be reported in a useful way. The units of measure between the base and sale price may be different.\r\n * @param css The set of css rules that can be applied.\r\n * @param price The price object\r\n */\r\n// #region HCL\r\nfunction renderAmbiguousPrice(css: CssClasses, price: AmbiguousDisplayPrice, testId: string, roundValue?: boolean) {\r\n\t// #endregion\r\n\t// conditionally cross out the base price\r\n\tconst basePriceClass = price.salePrice != null ? css.crossedOutPrice : css.normalPrice;\r\n\t// #region HCL\r\n\tconst roundedPrice = roundValue ? price.basePrice.rawValue.toFixed(2) : price.basePrice.value;\r\n\t// #endregion\r\n\treturn (\r\n\t\t<>\r\n\t\t\t\r\n\t\t\t\t{Localizer('OriginalPrice')}\r\n\t\t\t\t\r\n\t\t\t\t\t{/* #region HCL */}\r\n\t\t\t\t\t\r\n\t\t\t\t\t{/* #region HCL */}\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\t{price.salePrice != null ? (\r\n\t\t\t\t\r\n\t\t\t\t\t{Localizer('SalePrice')}\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t) : null}\r\n\t\t\r\n\t);\r\n}\r\n\r\n/**\r\n * Renders a partial display price (eg it may be either a simple price or a range price)\r\n */\r\nfunction VariantPriceComponent({ price }: { price: PartialDisplayPrice }) {\r\n\tconst label = price.unit?.label;\r\n\r\n\tswitch (typeof price.value) {\r\n\t\tcase 'string':\r\n\t\t\treturn ;\r\n\r\n\t\tcase 'object':\r\n\t\t\treturn ;\r\n\t}\r\n}\r\n\r\nfunction FormatAmbiguousPriceSpan(props: { price: Price; unitName: string }) {\r\n\t// Ambiguous prices always have a unit of measure. It is never optional.\r\n\treturn <>{Localizer('AmbiguousPriceRangeWithUnit', props.price, props.unitName)};\r\n}\r\n\r\n/**\r\n * Renders a price range (eg $14.99 - $19.99) with an optional unit of measure\r\n */\r\nfunction FormatPriceSpan(props: { priceRange: PriceSpan; unitName: string | undefined }) {\r\n\treturn props.unitName != null ? (\r\n\t\t<>\r\n\t\t\t{Localizer(\r\n\t\t\t\t'PriceRangeWithUnit',\r\n\t\t\t\tprops.priceRange.from,\r\n\t\t\t\tprops.priceRange.to,\r\n\t\t\t\tprops.unitName,\r\n\t\t\t)}\r\n\t\t\r\n\t) : (\r\n\t\t<>{Localizer('PriceRangeWithoutUnit', props.priceRange.from, props.priceRange.to)}\r\n\t);\r\n}\r\n\r\n/** Formats the price with the unit of measure (if present) appended. */\r\nfunction FormatPrice(props: { price: Price; unitName?: string }) {\r\n\treturn props.unitName != null ? (\r\n\t\t<>{Localizer('PriceWithUnit', props.price, props.unitName)}\r\n\t) : (\r\n\t\t<>{Localizer('PriceWithoutUnit', props.price)}\r\n\t);\r\n}\r\n","import { useState, useEffect } from 'react';\r\n\r\n/**\r\n * Reusable hook that returns true after a certain amount of time has passed. Useful to delay\r\n * displaying components in render functions.\r\n *\r\n * @export\r\n * @param {number} [waitMs=200]\r\n * @returns\r\n */\r\nexport default function useDelayedDisplay(waitMs: number = 200) {\r\n\tconst [display, setDisplay] = useState(false);\r\n\r\n\tuseEffect(() => {\r\n\t\t// after a set amount of time, show the spinner\r\n\t\tconst timeout = setTimeout(() => {\r\n\t\t\tsetDisplay(true);\r\n\t\t}, waitMs);\r\n\r\n\t\treturn () => {\r\n\t\t\tclearTimeout(timeout);\r\n\t\t};\r\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\r\n\t}, []);\r\n\r\n\treturn display;\r\n}\r\n","import { RocEventType, RocEvent, ExtractEventParameters } from 'shared/events';\r\n\r\n/**\r\n * Helper function for handling ROC events from non React functions.\r\n * @param eventType The type of event to handle\r\n * @param eventHandler The event handler\r\n * @returns The callback function to remove this handler\r\n */\r\nexport default function addRocEventHandler(\r\n\teventType: T,\r\n\teventHandler: (data: ExtractEventParameters) => void,\r\n): () => void {\r\n\tconst handler = (evt: CustomEvent): void => {\r\n\t\teventHandler(evt.detail);\r\n\t};\r\n\r\n\tdocument.addEventListener(eventType, handler);\r\n\r\n\treturn () => document.removeEventListener(eventType, handler);\r\n}\r\n","import { PricingResult } from './pricing-result';\r\n\r\n/**\r\n * Represents a display pricing service result containing all necessary details to render a set of product prices.\r\n */\r\nexport type DisplayPricingResult = PricingResult;\r\n\r\n/**\r\n * Represents a simple price intended to be rendered.\r\n * Comparisons and arithmetic between prices is illegal besides !== and ===.\r\n * This is not suitable for transactions and attempting to use this for transactions is erroneous.\r\n */\r\nexport type Price = string;\r\n\r\n/**\r\n * Represents an inclusive range of prices. It is erroneous for a price span no length (eg the\r\n * from and to field are the same), as this should be represented by a @see Price instead.\r\n *\r\n * PriceSpan is typically used for ambiguous ranges\r\n */\r\nexport type PriceSpan = {\r\n\t/**\r\n\t * The lower bound of the price. If this is associated with a group of products, this\r\n\t * is the price of the cheapest associated product.\r\n\t */\r\n\tfrom: Price;\r\n\r\n\t/**\r\n\t * The upper bound of the price. If this is associated with a group of products, this\r\n\t * is the price of the most expensive associated product.\r\n\t */\r\n\tto: Price;\r\n};\r\n\r\n/**\r\n * Represents a displayable price of unknown type.\r\n */\r\nexport type VariantPrice = Price | PriceSpan;\r\n\r\n/**\r\n * The display rules for rendering a price on the frontend.\r\n */\r\nexport enum PriceDisplayRule {\r\n\t/**\r\n\t * Represents a lack of a price. This is typically used for a product\r\n\t * that isn't purchaseable. Distinct from free.\r\n\t *\r\n\t * If a price is missing, it is illegal to use any display price type besides @see MissingPrice\r\n\t */\r\n\tMissing = 'Missing',\r\n\r\n\t/**\r\n\t * Represents a normal price or price range. Expected price types are @see PriceSpan and @see Price\r\n\t */\r\n\tNormal = 'Normal',\r\n\r\n\t/**\r\n\t * Represents a price without a clearly defined upperbound. This can happen if multiple products in a group product\r\n\t * do not share a common unit of measure. Expected price types are @see Price\r\n\t */\r\n\tAmbiguousRange = 'AmbiguousRange',\r\n}\r\n\r\n/**\r\n * Represents a pricing object that is guaranteed to have a base price\r\n */\r\nexport interface HasBasePrice {\r\n\t/** The price of the product if it isn't on sale. */\r\n\tbasePrice: PartialDisplayPrice;\r\n}\r\n\r\n/**\r\n * Represents a pricing object that is guaranteed to have a sale price\r\n */\r\nexport interface HasSalePrice {\r\n\t/** The sale price of the product. This is always less than the base price. */\r\n\tsalePrice: PartialDisplayPrice;\r\n}\r\n\r\n/**\r\n * Represents a pricing object that has no base price, that is, it cannot be purchased.\r\n */\r\nexport interface HasNoBasePrice {\r\n\t/** The item in question has no base price and as such, it is illegal to access it. */\r\n\tbasePrice?: never;\r\n}\r\n\r\n/**\r\n * Represents a pricing object that has no sale price\r\n */\r\nexport interface HasNoSalePrice {\r\n\t/** The item in question has no sale price and as such, it is illegal to access it. */\r\n\tsalePrice?: never;\r\n}\r\n\r\n/**\r\n * Represents a \"primitive\" price object that would be displayed.\r\n * A product that has a base price and a sale price would have two unit display prices for example.\r\n */\r\nexport type PartialDisplayPrice = TPrice extends Price\r\n\t? UnitPrice & HasRawValue\r\n\t: UnitPrice;\r\n\r\ninterface UnitPrice {\r\n\t/** The unit of measure of the item/product being sold. This is typically appended to a price to yield a value like \"$2.99/ea\" */\r\n\tunit?: { label: string };\r\n\r\n\t/** The price value to be displayed. */\r\n\tvalue: TPrice;\r\n\r\n\t// #region HCL - added availability to pass rawValue\r\n\trawValue?: number;\r\n\t// #endregion\r\n}\r\n\r\ninterface HasRawValue {\r\n\t/**\r\n\t * The raw price value for use in comparing prices to eachother or checking if they are zero or negative.\r\n\t * It is not safe to assume this value for display purposes or price calculations.\r\n\t */\r\n\trawValue: number;\r\n}\r\n\r\n/**\r\n * Constrains a VariantPrice to a more specific price type\r\n */\r\ntype Filter = T extends U ? T : never;\r\n\r\n/**\r\n * Represents a price object that can be displayed.\r\n */\r\nexport type DisplayPrice<\r\n\tTBasePrice extends VariantPrice = VariantPrice,\r\n\tTSalePrice extends VariantPrice = TBasePrice\r\n> =\r\n\t| SimpleDisplayPrice\r\n\t| AmbiguousDisplayPrice, Filter>\r\n\t| MissingDisplayPrice;\r\n\r\n/**\r\n * Represents a missing price, typically if used on an object not for a sale or an erroneous object.\r\n * This would either not be rendered or be rendered as \"Not For Sale\"\r\n */\r\nexport interface MissingDisplayPrice {\r\n\t/** The display rules for rendering a price on the frontend.\r\n\t *\r\n\t * A \"missing\" or \"invisible\" price is for a product that is not on sale and thus should not have\r\n\t * a price rendered on screen */\r\n\tdisplayRule: PriceDisplayRule.Missing;\r\n}\r\n\r\n/**\r\n * Represents a simple price, that is a price that might be on sale with fixed upper and lower bounds. The unit\r\n * of measure for the base and sale price (if present) is expected to be the same\r\n */\r\nexport interface SimpleDisplayPrice<\r\n\tTBasePrice extends VariantPrice = VariantPrice,\r\n\tTSalePrice extends VariantPrice = TBasePrice\r\n> extends HasBasePrice,\r\n\t\tPartial> {\r\n\t/** The display rules for rendering a price on the frontend.\r\n\t *\r\n\t * A normal price is either a simple price like \"$3.12/ea\" or a range like \"$2.00-$5.00/ea\"\r\n\t */\r\n\tdisplayRule: PriceDisplayRule.Normal;\r\n}\r\n\r\n/**\r\n * Represents a price without a predictable upperbound for the price. This would typically be used if a group of products\r\n * didn't have a common unit of measure.\r\n */\r\nexport interface AmbiguousDisplayPrice\r\n\textends HasBasePrice,\r\n\t\tPartial> {\r\n\t/** The display rules for rendering a price on the frontend.\r\n\t *\r\n\t * An ambiguous price doesn't have a a specifiable upperbound because there isn't a common unit of measure\r\n\t * to use as a baseline for what the most expensive unit of measure is. This is will appear like \"From $2.00\"\r\n\t */\r\n\tdisplayRule: PriceDisplayRule.AmbiguousRange;\r\n}\r\n","import { DisplayPricingResult, Price, SimpleDisplayPrice } from 'shared-client/types/commerce/display-price';\r\nimport { UnitOfMass } from 'shared-client/types/commerce/product-details';\r\nimport { BreadcrumbItem } from 'shared/components/Breadcrumbs';\r\nimport { PartialDataLoaderOptions } from 'shared/data/usePartialDataLoader';\r\nimport { BasePartialDetailsState } from 'shared/types';\r\nimport { Asset } from 'shared/assets/types';\r\n\r\nexport function getInitialProductDetailsState(): ProductDetailsState {\r\n\treturn window.__ROC_COMMERCE_PRODUCT_DETAILS__ ?? {};\r\n}\r\n\r\ndeclare global {\r\n\tinterface Window {\r\n\t\t__ROC_COMMERCE_PRODUCT_DETAILS__: ProductDetailsState;\r\n\t}\r\n}\r\n\r\nexport interface ProductDetailsState extends BasePartialDetailsState {\r\n\tsummary: Summary;\r\n\r\n\tassets?: ProductAsset[] | null;\r\n\r\n\tavailability?: Availability | null;\r\n\r\n\tbrand?: Brand | null;\r\n\r\n\t/**\r\n\t * Represents the visible tabs for the current product\r\n\t */\r\n\ttabs?: TabViewChunk[];\r\n\r\n\tselections?: ProductDetailSelection[];\r\n\r\n\tchildren?: SimpleProduct[];\r\n\r\n\trelatedProducts?: RelatedProduct[];\r\n\r\n\treviewsSummary: ReviewSummaryDto;\r\n\r\n\t/**\r\n\t * The available pricing lines for the product as well as the selected pricing line state\r\n\t */\r\n\tpricingLines?: ProductPricingLinesDetails;\r\n\r\n\t/**\r\n\t * The pricing result tree containing renderable prices for the product.\r\n\t */\r\n\tdisplayPricing?: DisplayPricingResult;\r\n\r\n\tbreadcrumbPath?: BreadcrumbItem[];\r\n\r\n\t/**\r\n\t * Information about the available product add ons for the product, and for child products, if this is a group\r\n\t */\r\n\taddOns?: AddOnsPdpDto;\r\n\r\n\tisNewProduct: boolean;\r\n}\r\n\r\n/**\r\n * Single add on data.\r\n */\r\nexport interface ProductAddOnDto {\r\n\t/**\r\n\t * The id of the product add on.\r\n\t */\r\n\tvalue: string;\r\n\r\n\t/**\r\n\t * The name of the product add on.\r\n\t */\r\n\tlabel: string;\r\n\r\n\t/**\r\n\t * The SKU of the product add on.\r\n\t */\r\n\tsku: string;\r\n\r\n\t/**\r\n\t * The description of the product add on.\r\n\t */\r\n\tdescription: string;\r\n\r\n\t/**\r\n\t * Represents if the current add on is required to add a product to cart.\r\n\t */\r\n\trequired: boolean;\r\n\r\n\t/**\r\n\t * Represents if the current add on is selected\r\n\t */\r\n\tselected: boolean;\r\n\r\n\t/**\r\n\t * Represents if the current add on is updated\r\n\t */\r\n\tisModified: boolean;\r\n\r\n\t/**\r\n\t * Represents the add on attributes\r\n\t */\r\n\tattributes: AddOnAttributeDto[];\r\n\r\n\t/**\r\n\t * The add on main image thumbnail.\r\n\t */\r\n\tthumbnailUrl: string;\r\n\r\n\t/**\r\n\t * The add on main image alt text.\r\n\t */\r\n\taltText: string;\r\n\r\n\t/**\r\n\t * The displayable price of a single unit of this line item.\r\n\t */\r\n\tunitPrice?: SimpleDisplayPrice;\r\n}\r\n\r\n/**\r\n * Product add on single attribute data\r\n */\r\nexport interface AddOnAttributeDto {\r\n\t/**\r\n\t * Product add on attribute id\r\n\t */\r\n\tvalue: string;\r\n\r\n\t/**\r\n\t * Product add on attribute name\r\n\t */\r\n\tlabel: string;\r\n\r\n\t/**\r\n\t * Represents if the add on attribute value is required\r\n\t */\r\n\trequired: boolean;\r\n\r\n\t/**\r\n\t * Product add on attribute instruction\r\n\t */\r\n\tinstructions: string;\r\n\r\n\t/**\r\n\t * Product add on attribute input type\r\n\t */\r\n\tinputAttributeType: InputAttributeType | null;\r\n\r\n\t/**\r\n\t * The maximum length constraint for the add on attribute value\r\n\t */\r\n\tmaximumLength: number | null;\r\n\r\n\t/**\r\n\t * The minimum length constraint for the add on attribute value\r\n\t */\r\n\tminimumLength: number | null;\r\n\r\n\t/**\r\n\t * The maximum number of line constraint for the add on attribute value\r\n\t * used for multiline attributes\r\n\t */\r\n\tmaximumNumberOfLines: number | null;\r\n\r\n\t/**\r\n\t * The default value of the add on attribute value\r\n\t */\r\n\tdefaultValue: string;\r\n\r\n\t/**\r\n\t * The entered value for the current attribute\r\n\t */\r\n\tdata: string;\r\n\r\n\t/**\r\n\t * The add on attribute selectable values\r\n\t */\r\n\tvalues: AttributeValueDto[];\r\n}\r\n\r\n/**\r\n * The add on attribute input type\r\n */\r\nexport enum InputAttributeType {\r\n\tSingle = 'Single',\r\n\tMultiline = 'Multiline',\r\n}\r\n\r\n/**\r\n * Single add on attribute selectable value\r\n */\r\nexport interface AttributeValueDto {\r\n\t/**\r\n\t * The add on attribute selectable value id\r\n\t */\r\n\tvalue: string;\r\n\r\n\t/**\r\n\t * The add on attribute selectable value label\r\n\t */\r\n\tlabel: string;\r\n\r\n\t/**\r\n\t * Represents if the current selectable value is selected\r\n\t */\r\n\tselected: boolean;\r\n}\r\n\r\nexport interface Summary {\r\n\tname: string;\r\n\ttype: ProductType;\r\n\tsku?: string;\r\n\tmanufacturerPartNumber?: string;\r\n\tproductUrl: string;\r\n\tdescription?: string;\r\n\tmeta?: Meta;\r\n\tproductGroupId: string | null;\r\n\t/**\r\n\t * \tFlag defining if the current product should use the alternate product details.\r\n\t */\r\n\tuseAlternateProductDetails?: boolean;\r\n\t// #region HCL\r\n\tproductGroupName?: string;\r\n\t// #endregion\r\n}\r\n\r\nexport interface Meta {\r\n\turl: string;\r\n\tpageTitle?: string;\r\n\tmetaKeywords?: string;\r\n\tmetaDescription?: string;\r\n\tnoIndex: boolean;\r\n\tnoFollow: boolean;\r\n}\r\n\r\nexport interface Availability {\r\n\tbackOrder: boolean;\r\n\toutOfStock: boolean;\r\n\t// #region HCL\r\n\tcallForAvailability: boolean;\r\n\t// #endregion\r\n}\r\n\r\n/**\r\n * Product Review Summary DTO\r\n */\r\nexport interface ReviewSummaryDto {\r\n\t/**\r\n\t * The average rating of the product. If undefined, the product has no reviews with ratings.\r\n\t */\r\n\taverageRating?: number;\r\n\r\n\t/**\r\n\t * The number of public reviews for the product\r\n\t */\r\n\treviewCount: number;\r\n}\r\n\r\nexport interface ProductAsset extends Asset {\r\n\t/**\r\n\t * The alternate text associated with the image\r\n\t */\r\n\taltText: string;\r\n\t/**\r\n\t * The main image URL\r\n\t */\r\n\tmainImageUrl: string;\r\n\t/**\r\n\t * The thumbnail URL\r\n\t */\r\n\tthumbnailUrl: string;\r\n\t/**\r\n\t * The enlarged image URL\r\n\t */\r\n\tenlargedImageUrl: string;\r\n\t/**\r\n\t * The sort order for this image\r\n\t */\r\n\tsortOrder: number;\r\n\t/**\r\n\t * Embedded URL for external media asset\r\n\t */\r\n\tembeddedUrl: string;\r\n}\r\n\r\nexport interface Brand {\r\n\tname: string;\r\n\tlogoUrl: string | null;\r\n}\r\n\r\nexport interface TabViewChunk {\r\n\ttab: Tab;\r\n\tmodel: T;\r\n\trawHtml?: string;\r\n}\r\n\r\nexport interface Tab {\r\n\tid: string;\r\n\tdisplayName: string;\r\n\ttabType: string;\r\n\tsortOrder: number;\r\n\tname: string;\r\n}\r\n\r\nexport interface TabViewModel {\r\n\tviewName: string;\r\n\tmodel: T;\r\n}\r\n\r\nexport interface ProductDetailSelection {\r\n\tlabel: string;\r\n\tvalues: SelectionValue[];\r\n\r\n\t/**\r\n\t * The selected attribute field name used for adding info to hawk context\r\n\t */\r\n\tfield: string;\r\n}\r\n\r\n/**\r\n * Represents a simple selection DTO object\r\n */\r\nexport interface SimpleSelectionDto {\r\n\t/**\r\n\t * The Code of the product attribute\r\n\t */\r\n\tid: string;\r\n\r\n\t/**\r\n\t * The Name of the product attribute\r\n\t */\r\n\tname: string;\r\n\r\n\t/**\r\n\t * The Id of the attribute value\r\n\t */\r\n\tvalue: string;\r\n\r\n\t/**\r\n\t * The Name of the attribute value\r\n\t */\r\n\tlabel: string;\r\n\r\n\t/**\r\n\t * The selected attribute field name used for adding info to hawk context\r\n\t */\r\n\tfield: string;\r\n}\r\n\r\nexport interface SelectionValue {\r\n\tvalue: string;\r\n\tlabel: string;\r\n\tvalid: boolean;\r\n\tchecked: boolean;\r\n\t// #region HCL\r\n\tswatchUrl: string;\r\n\t// #endregion\r\n}\r\n\r\nexport interface SimpleProduct {\r\n\tid: string;\r\n\tname: string;\r\n\t//#region HCL\r\n\tsku: string;\r\n\t//#endregion\r\n\tselections: string[];\r\n}\r\n\r\nexport interface RelatedProduct {\r\n\tid: string;\r\n\tname: string;\r\n\tdescription: string | null;\r\n\turl: string;\r\n\tmainImage: ProductAsset | null;\r\n}\r\n\r\nexport interface ProductSelectionDispatcherDto {\r\n\tproductId: string;\r\n\tproductDetails: ProductDetailsState;\r\n}\r\n\r\nexport interface AddOnsPdpDto {\r\n\t/**\r\n\t * The list of available product add-ons for the product.\r\n\t */\r\n\tproductAddOnData?: ProductAddOnDto[];\r\n\t/**\r\n\t * A list of add-ons available for child products, if this is a group product\r\n\t */\r\n\tavailableChildProductAddOns?: string[];\r\n}\r\n\r\n/*\r\n\tShirt Size: Small, Large\r\n\tColor: Red, Green\r\n\r\n\tScenario #1: All selections match\r\n\r\n\tValid selections:\r\n\tSmall, Red\r\n\tSmall, Green\r\n\tLarge, Red\r\n\tLarge, Green\r\n\r\n\t1. Pick Size\r\n\t2. Pick Color\r\n\t3. Find match and load child\r\n\r\n\tScenario #2: Selections mismatch\r\n\r\n\tValid selections:\r\n\tSmall, Red\r\n\tSmall, Green\r\n\tLarge, Red\r\n\r\n\t1. Pick Size (Small/Large)\r\n\t2. if Large, mark green as invalid\r\n\t3. if Small, show all colors\r\n\t4. Find match and load child\r\n\r\n\tScenario #3: Selections mismatch 2\r\n\r\n\tValid selections:\r\n\tSmall, Red\r\n\tSmall, Green\r\n\tLarge, Red\r\n\r\n\t1. Pick Green, mark large as invalid\r\n\t2. Pick Size\r\n\t3. Find match and load child\r\n\r\n\tScenario #4: Selections mismatch 3\r\n\r\n\tValid selections:\r\n\tSmall, Red\r\n\tSmall, Green\r\n\tLarge, Red\r\n\r\n\t1. Pick Green, mark large as invalid\r\n\t2. Pick Large anyway, remove color selection\r\n\t3. ...\r\n\t4. Find match and load child\r\n\r\n*/\r\n\r\n/**\r\n * All available product types\r\n *\r\n * @export\r\n */\r\nexport enum ProductType {\r\n\tSimple = 'Simple',\r\n\tGroup = 'Group',\r\n\tKit = 'Kit',\r\n\tGiftCard = 'GiftCard',\r\n}\r\n\r\nexport type ProductPartialDataLoaderOptions = Pick<\r\n\tPartialDataLoaderOptions,\r\n\t'initialState' | 'dataLoaderFn'\r\n>;\r\n\r\nexport interface ProductSelection {\r\n\t/**\r\n\t * The code of the product attribute\r\n\t */\r\n\tid: string;\r\n\t/**\r\n\t * The name of the product attribute\r\n\t */\r\n\tname: string;\r\n\t/**\r\n\t * The attribute value name (if available), otherwise attribute value value\r\n\t */\r\n\tlabel: string;\r\n\t/**\r\n\t * The id (Guid) of the attribute value\r\n\t */\r\n\tvalue: string;\r\n\r\n\t/**\r\n\t * The selected attribute field name used for adding info to hawk context\r\n\t */\r\n\tfield: string;\r\n}\r\n\r\n/**\r\n * Base unit of measure\r\n */\r\nexport interface UnitOfMeasure {\r\n\tname: string;\r\n\tdisplayName: string;\r\n}\r\n\r\n/**\r\n * Represents a single pricing line for a product (one of potentially many choices for purchasing a product)\r\n */\r\nexport type ProductPricingLine = {\r\n\t/**\r\n\t * The pricing line's id\r\n\t */\r\n\tid: string;\r\n\r\n\t/**\r\n\t *\r\n\t * Unit of measure id\r\n\t */\r\n\tunitOfMeasureId: string;\r\n\r\n\t/**\r\n\t * Base Unit of measure\r\n\t */\r\n\tunitOfMeasure: UnitOfMeasure;\r\n\r\n\t/**\r\n\t * The unit's of measure display name\r\n\t */\r\n\tlabel: string;\r\n\r\n\t/**\r\n\t * The unit's of measure quantity\r\n\t */\r\n\tquantity: number;\r\n\r\n\t/**\r\n\t * If this pricing line isn't available for purchase, this will contain a string-localizer-key explaining the reason why.\r\n\t */\r\n\tunavailabilityReason: string | undefined;\r\n} & MassPricingLineOptions;\r\n\r\ntype MassPricingLineOptions =\r\n\t| {\r\n\t\t\t/**\r\n\t\t\t * The unit's of measure weight unit\r\n\t\t\t */\r\n\t\t\tunitOfMass: UnitOfMass;\r\n\r\n\t\t\t/**\r\n\t\t\t * The unit's of measure weight\r\n\t\t\t */\r\n\t\t\tweight: number;\r\n\t }\r\n\t| {\r\n\t\t\tunitOfMass?: never;\r\n\t\t\tweight?: never;\r\n\t };\r\n\r\n/**\r\n * Represents state details pricing lines and pricing line selections for a product on a product details page.\r\n */\r\nexport interface ProductPricingLinesDetails {\r\n\t/**\r\n\t * The selected pricing lines\r\n\t */\r\n\tselected?: ProductPricingLine;\r\n\t/**\r\n\t * The array of available pricing lines\r\n\t */\r\n\tavailable: ProductPricingLine[];\r\n}\r\n\r\n/**\r\n * Reason that the exception was thrown when adding an item to the cart\r\n */\r\nexport enum AddToCartExceptionCode {\r\n\tgeneric = 'Generic',\r\n\tcartFull = 'CartFull',\r\n\tproductUnavailable = 'ProductUnavailable',\r\n\tproductOutOfStock = 'ProductOutOfStock',\r\n}\r\n\r\n/**\r\n * Interface used as request for getting bulk products partial data\r\n */\r\nexport interface BaseBulkPartialProductsDataOptions {\r\n\tproductIds: string[] | undefined;\r\n\tmissingKeys: Array;\r\n}\r\n","import React from 'react';\r\nimport { initReactAppByAttribute } from 'shared/util/ApplicationInit';\r\nimport RecentlyViewedProducts from './RecentlyViewedProducts';\r\n\r\nconst render = initReactAppByAttribute(() => , 'data-recently-viewed-products');\r\n\r\nrender();\r\n\r\n// optional, use this to enable hot reloading\r\nif ((module as any).hot) {\r\n\t(module as any).hot.accept('./RecentlyViewedProducts', () => {\r\n\t\trender();\r\n\t});\r\n}\r\n"],"sourceRoot":""}