{"version":3,"file":"productPage.3ae75dc89aad5da49d40.js","sources":["webpack:///./Source/FECOM.Web.UI/Scripts/apps/Offer/OfferPrompt.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/apps/product/ProductPage.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/apps/product/components/NavLinks.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/apps/product/components/OutOfStockForm.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/apps/product/components/ProductLessons.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/apps/product/components/Variants.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/apps/save-for-later/AddToSaveForLater.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/apps/save-for-later/SaveForLaterApi.ts","webpack:///./Source/FECOM.Web.UI/Scripts/apps/trustpilot/AverageStarRating.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/apps/trustpilot/TrustBox.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/apps/trustpilot/trustPilotApi.ts","webpack:///./Source/FECOM.Web.UI/Scripts/components/Accordion.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/Alert.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/Button.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/HtmlText.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/Icon.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/JsonView.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/Loader.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/LoaderMask.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/Modal.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/Product.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/Roundels.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/Select.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/StockCtas.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/components/TextInput.tsx","webpack:///./Source/FECOM.Web.UI/Scripts/helpers/saveforlater.ts","webpack:///./Source/FECOM.Web.UI/Scripts/utilities/canUseDOM.ts","webpack:///./Source/FECOM.Web.UI/Scripts/utilities/formatCurrency.ts","webpack:///./Source/FECOM.Web.UI/Scripts/utilities/scrollToId.ts","webpack:///./Source/FECOM.Web.UI/Scripts/utilities/updateQuantityBadge.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\r\nimport { Button } from '../../components/Button';\r\nimport { OfferTypes } from '../../types/enums';\r\nimport { ProductViewModel } from '../../types/generated';\r\nimport { getProductQuantityInBasket } from '../product/productPageApi';\r\nimport { OfferDetails } from './Offer';\r\n\r\nexport default function OfferPrompt({\r\n offer,\r\n handleQuantityChange,\r\n basketQty,\r\n className,\r\n offerFreeProduct\r\n}: OfferPromptProps) {\r\n const [showPrompt, setShowPrompt] = useState(false);\r\n const [showAddFreeItemPrompt, setShowAddFreeItemPrompt] =\r\n useState(false);\r\n const [quantityNeededToGetOffer, setQuantityNeededToGetOffer] =\r\n useState(0);\r\n const [addToBasketQty, setAddToBasketQty] = useState(0);\r\n const [freeItemBasketQty, setFreeItemBasketQty] = useState(-1);\r\n const [eligibleFreeItemQty, setEligibleFreeItemQty] = useState(0);\r\n const halfWayPoint = Math.round(offer.offerQuantityBuy / 2);\r\n\r\n useEffect(() => {\r\n setShowPrompt(false);\r\n setShowAddFreeItemPrompt(false);\r\n\r\n if (\r\n quantityNeededToGetOffer > 0 &&\r\n ((offer.offerQuantityBuy % 2 !== 0 &&\r\n quantityNeededToGetOffer < halfWayPoint) ||\r\n (offer.offerQuantityBuy % 2 == 0 &&\r\n quantityNeededToGetOffer <= halfWayPoint))\r\n ) {\r\n setShowPrompt(true);\r\n } else if (\r\n eligibleFreeItemQty > 0 &&\r\n (offer.offerTypeName === OfferTypes.ExtraFree ||\r\n (offer.offerTypeName === OfferTypes.FreeProduct &&\r\n freeItemBasketQty >= 0))\r\n ) {\r\n setShowAddFreeItemPrompt(true);\r\n }\r\n }, [\r\n eligibleFreeItemQty,\r\n quantityNeededToGetOffer,\r\n halfWayPoint,\r\n offer.offerQuantityBuy,\r\n freeItemBasketQty,\r\n offer.offerTypeName\r\n ]);\r\n\r\n useEffect(() => {\r\n if (offer.offerTypeName === OfferTypes.ExtraFree) {\r\n setAddToBasketQty(quantityNeededToGetOffer + offer.offerQuantityFree);\r\n } else if (offer.offerTypeName === OfferTypes.FreeProduct) {\r\n setAddToBasketQty(quantityNeededToGetOffer);\r\n }\r\n }, [offer.offerTypeName, offer.offerQuantityFree, quantityNeededToGetOffer]);\r\n\r\n useEffect(() => {\r\n if (offerFreeProduct?.catalogueRef) {\r\n getProductQuantityInBasket(offerFreeProduct.catalogueRef).then(\r\n (result: number) => {\r\n setFreeItemBasketQty(result);\r\n }\r\n );\r\n }\r\n }, [offerFreeProduct]);\r\n\r\n useEffect(() => {\r\n let qtyNeededToGetOffer: number = offer.offerQuantityBuy - basketQty;\r\n let eligibleFreeItmQty: number = 0;\r\n let basketQuantity = basketQty;\r\n if (basketQty > 0) {\r\n if (offer.offerTypeName === OfferTypes.ExtraFree) {\r\n const qtyWithOfferItem =\r\n offer.offerQuantityBuy + offer.offerQuantityFree;\r\n if (basketQty > offer.offerQuantityBuy) {\r\n while (basketQuantity >= qtyWithOfferItem) {\r\n basketQuantity -= qtyWithOfferItem;\r\n }\r\n\r\n if (basketQuantity === offer.offerQuantityBuy) {\r\n eligibleFreeItmQty = offer.offerQuantityFree;\r\n } else if (basketQuantity > offer.offerQuantityBuy) {\r\n eligibleFreeItmQty = qtyWithOfferItem - basketQuantity;\r\n }\r\n\r\n qtyNeededToGetOffer = offer.offerQuantityBuy - basketQuantity;\r\n } else if (basketQty === offer.offerQuantityBuy) {\r\n eligibleFreeItmQty = offer.offerQuantityFree;\r\n }\r\n } else if (offer.offerTypeName === OfferTypes.FreeProduct) {\r\n if (basketQty > offer.offerQuantityBuy) {\r\n while (basketQuantity >= offer.offerQuantityBuy) {\r\n basketQuantity -= offer.offerQuantityBuy;\r\n eligibleFreeItmQty += offer.offerQuantityFree;\r\n }\r\n\r\n if (basketQuantity > 0) {\r\n qtyNeededToGetOffer = offer.offerQuantityBuy - basketQuantity;\r\n }\r\n\r\n if (!isNaN(freeItemBasketQty) && freeItemBasketQty >= 0)\r\n eligibleFreeItmQty -= freeItemBasketQty;\r\n } else if (\r\n basketQty === offer.offerQuantityBuy &&\r\n !isNaN(freeItemBasketQty) &&\r\n freeItemBasketQty < offer.offerQuantityFree\r\n ) {\r\n eligibleFreeItmQty = offer.offerQuantityFree - freeItemBasketQty;\r\n }\r\n }\r\n\r\n setQuantityNeededToGetOffer(qtyNeededToGetOffer);\r\n setEligibleFreeItemQty(eligibleFreeItmQty);\r\n }\r\n }, [\r\n basketQty,\r\n offer.offerQuantityBuy,\r\n offer.offerQuantityFree,\r\n offer.offerTypeName,\r\n eligibleFreeItemQty,\r\n quantityNeededToGetOffer,\r\n freeItemBasketQty\r\n ]);\r\n\r\n return (\r\n
\r\n {showPrompt && (\r\n {\r\n if (offer.productCatalogueRef) {\r\n if (offer.offerTypeName === OfferTypes.ExtraFree) {\r\n handleQuantityChange(addToBasketQty, offer.productCatalogueRef);\r\n } else if (offer.offerTypeName === OfferTypes.FreeProduct) {\r\n if (offer.productCatalogueRef && addToBasketQty > 0) {\r\n handleQuantityChange(\r\n addToBasketQty,\r\n offer.productCatalogueRef\r\n );\r\n if (offerFreeProduct && offerFreeProduct.catalogueRef) {\r\n const newQuantity =\r\n offer.offerQuantityFree + eligibleFreeItemQty;\r\n handleQuantityChange(\r\n newQuantity,\r\n offerFreeProduct.catalogueRef\r\n );\r\n setFreeItemBasketQty(\r\n (freeItemBasketQty) => freeItemBasketQty + newQuantity\r\n );\r\n }\r\n }\r\n }\r\n\r\n setShowPrompt(false);\r\n setShowAddFreeItemPrompt(false);\r\n setQuantityNeededToGetOffer(offer.offerQuantityBuy);\r\n setEligibleFreeItemQty(0);\r\n }\r\n }}\r\n >\r\n Add {quantityNeededToGetOffer} more to get your FREE item\r\n \r\n )}\r\n\r\n {showAddFreeItemPrompt && (\r\n {\r\n if (offer.productCatalogueRef) {\r\n if (offer.offerTypeName === OfferTypes.ExtraFree) {\r\n handleQuantityChange(\r\n eligibleFreeItemQty,\r\n offer.productCatalogueRef\r\n );\r\n } else if (offer.offerTypeName === OfferTypes.FreeProduct) {\r\n if (offerFreeProduct && offerFreeProduct.catalogueRef) {\r\n handleQuantityChange(\r\n eligibleFreeItemQty,\r\n offerFreeProduct.catalogueRef\r\n );\r\n setFreeItemBasketQty(\r\n (freeItemBasketQty) =>\r\n freeItemBasketQty + eligibleFreeItemQty\r\n );\r\n }\r\n }\r\n setShowPrompt(false);\r\n setShowAddFreeItemPrompt(false);\r\n setQuantityNeededToGetOffer(offer.offerQuantityBuy);\r\n setEligibleFreeItemQty(0);\r\n }\r\n }}\r\n >\r\n Add your FREE item to basket\r\n \r\n )}\r\n
\r\n );\r\n}\r\n\r\nexport interface OfferPromptProps {\r\n offer: OfferDetails;\r\n handleQuantityChange: (qty: number, catalogueRef: string) => void;\r\n basketQty: number;\r\n className: string;\r\n offerFreeProduct: ProductViewModel | null;\r\n}\r\n","import { ProductState } from '../../types/generated';\r\nimport { Router, RouteComponentProps } from '@reach/router';\r\nimport canUseDOM from '../../utilities/canUseDOM';\r\nimport LoaderMask from '../../components/LoaderMask';\r\nimport Product from '../../components/Product';\r\n\r\nexport interface ProductPageProps extends ProductState {}\r\n\r\nexport default function ProductPage({\r\n transactionalCurrency,\r\n product,\r\n isLoggedIn,\r\n baseUrl,\r\n showMoreOptions,\r\n showSecondaryPrice,\r\n showProductReviews,\r\n showDocuments,\r\n showFeatures,\r\n showUsefulLinks,\r\n trustPilotBusinessUnitId,\r\n website,\r\n isCssFavouritesEnabled\r\n}: ProductPageProps) {\r\n if (!product) {\r\n return null;\r\n //todo: return an error message\r\n }\r\n\r\n // const basePath = baseUrl ? baseUrl.replace(/(((\\/)+$)|(\\?f=.*))/, '') : '/';\r\n // const basePath = baseUrl ? baseUrl.replace(/\\/(?!.*\\/).*/, '') : '/';\r\n const basePath = baseUrl || '/';\r\n\r\n // console.log('basePath', basePath);\r\n\r\n /**\r\n * Render\r\n */\r\n\r\n if (!canUseDOM()) {\r\n return (\r\n }\r\n path={`${basePath}/:catalogueRefParam`}\r\n baseUrl={basePath}\r\n showMoreOptions={showMoreOptions}\r\n product={product}\r\n isLoggedIn={isLoggedIn}\r\n transactionalCurrency={transactionalCurrency}\r\n showSecondaryPrice={showSecondaryPrice}\r\n showProductReviews={showProductReviews}\r\n showDocuments={showDocuments}\r\n showFeatures={showFeatures}\r\n showUsefulLinks={showUsefulLinks}\r\n trustPilotBusinessUnitId={trustPilotBusinessUnitId}\r\n quantity={product.baseQuantity}\r\n website={website}\r\n isCssFavouritesEnabled={isCssFavouritesEnabled}\r\n />\r\n );\r\n }\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n );\r\n}\r\n\r\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\r\nconst NotFound = ({ ...props }: RouteComponentProps) => (\r\n
\r\n

Not Found

\r\n
\r\n);\r\n","import Icon from '../../../components/Icon';\r\nimport { scrollToId, scrollToNestedId } from '../../../utilities/scrollToId';\r\n\r\ninterface NavLinksProps {\r\n reasonsToLove: boolean;\r\n learningOutcomes: boolean;\r\n hasProductLessons: boolean;\r\n topTips: boolean;\r\n}\r\n\r\nexport default function NavLinks({\r\n reasonsToLove,\r\n learningOutcomes,\r\n hasProductLessons,\r\n topTips\r\n}: NavLinksProps) {\r\n return (\r\n <>\r\n {\r\n e.preventDefault();\r\n scrollToId('descriptionTab', 'product-tab--selected');\r\n }}\r\n >\r\n Product Description\r\n \r\n \r\n {hasProductLessons && (\r\n {\r\n e.preventDefault();\r\n scrollToId('productLessons', 'product-details__product-lessons');\r\n }}\r\n >\r\n Lessons and Lesson Resources\r\n \r\n \r\n )}\r\n\r\n {reasonsToLove && (\r\n {\r\n e.preventDefault();\r\n scrollToNestedId(\r\n 'descriptionTab',\r\n 'product-tab--selected',\r\n 'reasonsToLoveLink'\r\n );\r\n }}\r\n >\r\n Reasons to Love\r\n \r\n \r\n )}\r\n\r\n {learningOutcomes && (\r\n {\r\n e.preventDefault();\r\n scrollToNestedId(\r\n 'descriptionTab',\r\n 'product-tab--selected',\r\n 'learningOutcomesLink'\r\n );\r\n }}\r\n >\r\n Learning Outcomes\r\n \r\n \r\n )}\r\n\r\n {topTips && (\r\n {\r\n e.preventDefault();\r\n scrollToNestedId(\r\n 'descriptionTab',\r\n 'product-tab--selected',\r\n 'topTipsLink'\r\n );\r\n }}\r\n >\r\n Top Tips\r\n \r\n \r\n )}\r\n \r\n );\r\n}\r\n","import { ReactElement } from 'react';\r\nimport {\r\n Field,\r\n FieldProps,\r\n Form,\r\n Formik,\r\n FormikHelpers,\r\n FormikProps,\r\n FormikValues\r\n} from 'formik';\r\nimport * as Yup from 'yup';\r\nimport { InStockNotificationRequest } from '../../../types/generated';\r\nimport Loader from '../../../components/Loader';\r\nimport TextInput from '../../../components/TextInput';\r\nimport { registerForInStockNotification } from '../productPageApi';\r\nimport Modal from '../../../components/Modal';\r\nimport popupAlert from '../../../utilities/popup-alert';\r\nimport Icon from '../../../components/Icon';\r\n\r\ntype FormValues = Partial;\r\n\r\nconst validationSchema = Yup.object().shape({\r\n firstName: Yup.string().label('First Name').required(),\r\n lastName: Yup.string().label('Last Name').required(),\r\n emailAddress: Yup.string().label('Email Address').email().required()\r\n});\r\n\r\ninterface OutOfStockFormProps {\r\n catalogueRef: string | null;\r\n showModal: boolean;\r\n handleClose: () => void;\r\n}\r\n\r\nexport default function OutOfStockForm({\r\n catalogueRef,\r\n showModal,\r\n handleClose\r\n}: OutOfStockFormProps): ReactElement {\r\n const initialValues: FormValues = {\r\n firstName: '',\r\n lastName: '',\r\n emailAddress: '',\r\n catalogueRef\r\n };\r\n\r\n function handleSubmit(values: FormikValues, errorCallback: () => void) {\r\n const form: FormValues = {\r\n ...values\r\n };\r\n\r\n registerForInStockNotification(form)\r\n .then((data) => {\r\n if (data.success) {\r\n // if change successful\r\n handleClose();\r\n popupAlert.showPositive('Your details have been sent successfully');\r\n } else {\r\n // if change unsuccessful\r\n errorCallback();\r\n popupAlert.showNegative(\r\n 'There was a problem submitting your details, please try again'\r\n );\r\n }\r\n })\r\n .catch((err: Error) => {\r\n errorCallback();\r\n popupAlert.showNegative(\r\n 'There was a problem submitting your details, please try again'\r\n );\r\n console.error(err.message);\r\n });\r\n }\r\n\r\n return (\r\n \r\n \r\n ) => {\r\n handleSubmit(values, () => {\r\n setSubmitting(false);\r\n });\r\n }}\r\n >\r\n {(formikBag: FormikProps) =>\r\n formikBag.isSubmitting ? (\r\n \r\n ) : (\r\n <>\r\n \r\n \r\n \r\n

\r\n Please provide us with your name and email address and we will\r\n send you an email to let you know when this item is back in\r\n stock.\r\n

\r\n
\r\n \r\n {({\r\n field,\r\n form: { touched, errors }\r\n }: FieldProps) => (\r\n \r\n )}\r\n \r\n \r\n {({\r\n field,\r\n form: { touched, errors }\r\n }: FieldProps) => (\r\n \r\n )}\r\n \r\n \r\n {({\r\n field,\r\n form: { touched, errors }\r\n }: FieldProps) => (\r\n \r\n )}\r\n \r\n
\r\n
\r\n \r\n Cancel\r\n \r\n \r\n
\r\n
\r\n
\r\n \r\n )\r\n }\r\n \r\n \r\n );\r\n}\r\n","import * as React from 'react';\r\nimport { ProductLessons as ProductLessonsModel } from '../../../types/generated';\r\nimport { Lesson } from '../../../types/generated';\r\n\r\ninterface ProductLessonProps {\r\n productLessons: ProductLessonsModel;\r\n}\r\n\r\nexport default function ProductLessons({\r\n productLessons\r\n}: ProductLessonProps): React.ReactElement {\r\n return (\r\n \r\n );\r\n}\r\n\r\ninterface AdditionalLessonsProps {\r\n productLessons: ProductLessonsModel;\r\n}\r\n\r\nfunction AdditionalLessons({ productLessons }: AdditionalLessonsProps) {\r\n const searchUrl = productLessons.searchUrl;\r\n return (\r\n
\r\n {productLessons.additionalLessonCount > 0 && (\r\n \r\n More...\r\n \r\n )}\r\n
\r\n Links will open in EuHu\r\n
\r\n
\r\n );\r\n}\r\n\r\ninterface LessonKeyStageProps {\r\n keyStages: string[];\r\n}\r\n\r\nfunction LessonKeyStages({ keyStages }: LessonKeyStageProps) {\r\n if (!keyStages || keyStages.length === 0) {\r\n return null;\r\n }\r\n\r\n const keyStageElements = keyStages.map((keyStage) => {\r\n if (keyStage !== 'undefined' && keyStage === 'KEY STAGE 1') {\r\n return (\r\n
\r\n );\r\n }\r\n\r\n if (keyStage !== 'undefined' && keyStage === 'KEY STAGE 2') {\r\n return (\r\n
\r\n );\r\n }\r\n\r\n if (keyStage !== 'undefined' && keyStage === 'EYFS') {\r\n return (\r\n
\r\n );\r\n }\r\n\r\n return <>;\r\n });\r\n\r\n return (\r\n <>\r\n {keyStageElements.map((keyStage) => (\r\n <>{keyStage}\r\n ))}\r\n \r\n );\r\n}\r\n","import { Select } from '../../../components/Select';\r\nimport { ProductVariants } from '../../../types/generated';\r\n\r\nexport interface VariantsProps {\r\n onChange: (facetGroupId: number, facetId: number | null) => void;\r\n variants?: ProductVariants | null;\r\n}\r\n\r\nexport function Variants({ variants, onChange }: VariantsProps) {\r\n if (!variants || !variants.facets) {\r\n return null;\r\n }\r\n\r\n const facets = variants.facets;\r\n\r\n function changeSelection(facetGroupId: number, facetId: number | null) {\r\n onChange(facetGroupId, facetId);\r\n }\r\n\r\n return (\r\n
\r\n {facets.map((facetGroup) => {\r\n if (!facetGroup || !facetGroup.facetValues) {\r\n return null;\r\n }\r\n\r\n const selectedFacet = facetGroup.facetValues.find((f) => f.isSelected);\r\n\r\n return (\r\n {\r\n const id = parseInt(e.target.value, 10);\r\n\r\n changeSelection(facetGroup.id, id || null);\r\n }}\r\n labelClassName=\"col-4 col-sm-3 col-lg-4\"\r\n inputClassName=\"col-8 col-sm-7 col-lg-8 align-self-center\"\r\n value={(selectedFacet && selectedFacet.facetValueId) || -1}\r\n >\r\n \r\n ))}\r\n \r\n );\r\n })}\r\n\r\n {/* Mobile/tablet */}\r\n {variants?.hasNoProductForCombination && (\r\n
\r\n

\r\n No product is currently available for this combination\r\n

\r\n
\r\n )}\r\n
\r\n );\r\n}\r\n\r\n// const dummyVariants: DummyVariants = {\r\n// variantHeaderCatalogRef: 'g1761266',\r\n// variantCatalogRef: null,\r\n// hasVariantProduct: false,\r\n// facets: [\r\n// {\r\n// name: 'Colour',\r\n// id: 121,\r\n// facetValues: [\r\n// {\r\n// facetValueId: 57242,\r\n// facetValue: 'Black',\r\n// isSelected: false,\r\n// },\r\n// {\r\n// facetValueId: 57243,\r\n// facetValue: 'Blue',\r\n// isSelected: false,\r\n// },\r\n// {\r\n// facetValueId: 57247,\r\n// facetValue: 'Green',\r\n// isSelected: false,\r\n// },\r\n// {\r\n// facetValueId: 57254,\r\n// facetValue: 'Orange',\r\n// isSelected: false,\r\n// },\r\n// {\r\n// facetValueId: 57259,\r\n// facetValue: 'Red',\r\n// isSelected: false,\r\n// },\r\n// ],\r\n// },\r\n// {\r\n// name: 'Manufactures Size by Inches (Chest)',\r\n// id: 2893,\r\n// facetValues: [\r\n// {\r\n// facetValueId: 60037,\r\n// facetValue: '50-52in',\r\n// isSelected: false,\r\n// },\r\n// {\r\n// facetValueId: 60038,\r\n// facetValue: '50-54in',\r\n// isSelected: false,\r\n// },\r\n// ],\r\n// },\r\n// ],\r\n// };\r\n\r\n// export interface DummyVariants {\r\n// variantHeaderCatalogRef: string;\r\n// variantCatalogRef: null;\r\n// hasVariantProduct: boolean;\r\n// facets: Facet[];\r\n// }\r\n\r\n// export interface Facet {\r\n// name: string;\r\n// id: number;\r\n// facetValues: FacetValue[];\r\n// }\r\n\r\n// export interface FacetValue {\r\n// facetValueId: number;\r\n// facetValue: string;\r\n// isSelected: boolean;\r\n// }\r\n","import { useState, useEffect } from 'react';\r\nimport popupAlert from '../../utilities/popup-alert';\r\nimport {\r\n checkItemInSaveForLater,\r\n addItemToSaveForLater,\r\n removeItemFromSaveForLater\r\n} from './SaveForLaterApi';\r\nimport LoaderMask from '../../components/LoaderMask';\r\nimport Loader from '../../components/Loader';\r\nimport Icon from '../../components/Icon';\r\nimport { SaveForLaterResponse } from '../../types/generated';\r\nimport { updateSavedItemsCount } from '../../helpers/saveforlater';\r\n\r\ninterface AddToSaveForLaterProps {\r\n catalogueRef?: string | null;\r\n modifier?: string | null;\r\n buttonTitle?: string;\r\n size: '' | 'sm' | 'lg';\r\n quantity?: number | '';\r\n}\r\n\r\nexport default function AddToSaveForLater({\r\n catalogueRef,\r\n quantity,\r\n buttonTitle,\r\n modifier\r\n}: AddToSaveForLaterProps) {\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [isSavedForLater, setIsSavedForLater] = useState(false);\r\n\r\n useEffect(() => {\r\n if (catalogueRef) {\r\n setIsLoading(true);\r\n checkItemInSaveForLater(catalogueRef).then(\r\n (data: SaveForLaterResponse) => {\r\n if (data?.isSuccess) {\r\n setIsLoading(false);\r\n setIsSavedForLater(true);\r\n } else {\r\n setIsLoading(false);\r\n setIsSavedForLater(false);\r\n }\r\n }\r\n );\r\n }\r\n }, [catalogueRef]);\r\n\r\n function handleAddItemToSaveForLater() {\r\n let newQuantity = 1;\r\n\r\n if (quantity) {\r\n if (isNaN(quantity) || quantity < 1) {\r\n popupAlert.showNegative(\r\n 'Please check the quantity field, only positive numbers are allowed.'\r\n );\r\n return;\r\n } else {\r\n newQuantity = quantity;\r\n }\r\n }\r\n addItemToSaveForLater(catalogueRef || null, modifier || null, newQuantity)\r\n .then((data: SaveForLaterResponse) => {\r\n if (data?.isSuccess) {\r\n updateSavedItemsCount(data.itemCount);\r\n setIsSavedForLater(true);\r\n popupAlert.show(\r\n 'Item added to save for later',\r\n popupAlert.types.positive,\r\n popupAlert.icons.positive,\r\n 5000,\r\n 'View saved items',\r\n '/saved-for-later'\r\n );\r\n } else {\r\n popupAlert.showNegative('Failed to add item to save for later');\r\n }\r\n })\r\n .catch((error: Error) => {\r\n console.log('err', error);\r\n popupAlert.showNegative('Failed to add item to save for later');\r\n });\r\n }\r\n\r\n function handleRemoveItemFromSaveForLater() {\r\n removeItemFromSaveForLater(catalogueRef || null)\r\n .then((data: SaveForLaterResponse) => {\r\n if (data?.isSuccess) {\r\n updateSavedItemsCount(data.itemCount);\r\n setIsSavedForLater(false);\r\n popupAlert.showPositive('Item removed from save for later');\r\n } else {\r\n popupAlert.showNegative('Failed to remove item from save for later');\r\n }\r\n })\r\n .catch((error: Error) => {\r\n console.log('err', error);\r\n popupAlert.showNegative('Failed to remove item from save for later');\r\n });\r\n }\r\n\r\n return (\r\n
\r\n {isLoading ? (\r\n \r\n \r\n \r\n ) : (\r\n <>\r\n
\r\n {isSavedForLater ? (\r\n \r\n \r\n {' '}\r\n \r\n {buttonTitle || 'Saved for Later'}\r\n \r\n ) : (\r\n \r\n \r\n {' '}\r\n \r\n {buttonTitle || 'Save for Later'}\r\n \r\n )}\r\n
\r\n\r\n
\r\n {isSavedForLater ? (\r\n \r\n \r\n {' '}\r\n \r\n {buttonTitle || 'Saved for Later'}\r\n \r\n ) : (\r\n \r\n \r\n {' '}\r\n \r\n {buttonTitle || 'Save for Later'}\r\n \r\n )}\r\n
\r\n \r\n )}\r\n
\r\n );\r\n}\r\n","const apiBasePath = '/umbraco/surface/WishlistSurface/';\r\n\r\nconst fetchBaseConfig: RequestInit = {\r\n credentials: 'same-origin',\r\n headers: {\r\n 'content-type': 'application/json',\r\n accept: 'application/json',\r\n },\r\n};\r\n\r\nexport const addItemToSaveForLater = (\r\n catalogueRef: string | null,\r\n modifier: string | null,\r\n quantity: number\r\n) => {\r\n const path = `${apiBasePath}AddItemToSaveForLater`;\r\n\r\n return fetch(path, {\r\n ...fetchBaseConfig,\r\n method: 'POST',\r\n body: JSON.stringify({\r\n catalogueRef,\r\n modifier,\r\n quantity\r\n }),\r\n }).then(response => response.json());\r\n};\r\n\r\nexport const removeItemFromSaveForLater = (catalogueRef: string | null) => {\r\n const path = `${apiBasePath}RemoveFromSaveForLater`;\r\n\r\n return fetch(path, {\r\n ...fetchBaseConfig,\r\n method: 'POST',\r\n body: JSON.stringify({\r\n catalogueRef\r\n }),\r\n }).then(response => response.json());\r\n};\r\n\r\nexport const checkItemInSaveForLater = (catalogueRef: string) => {\r\n const path = `${apiBasePath}CheckItemInSaveForLater`;\r\n\r\n return fetch(path, {\r\n ...fetchBaseConfig,\r\n method: 'POST',\r\n body: JSON.stringify({\r\n catalogueRef\r\n }),\r\n }).then(response => response.json());\r\n};\r\n\r\nexport const updateSavedForLaterItemQuantity = (catalogueRef: string, quantity: number) => {\r\n const path = `${apiBasePath}UpdateSavedForLaterItemQuantity`;\r\n\r\n return fetch(path, {\r\n ...fetchBaseConfig,\r\n method: 'POST',\r\n body: JSON.stringify({\r\n catalogueRef,\r\n quantity\r\n }),\r\n }).then(response => response.json());\r\n};\r\n\r\nexport const addSavedForLaterItemsToBasket = () => {\r\n const path = `${apiBasePath}AddSavedForLaterItemsToBasket`;\r\n\r\n return fetch(path, {\r\n ...fetchBaseConfig,\r\n method: 'POST'\r\n }).then(response => response.json());\r\n};\r\n","import { scrollToId } from '../../utilities/scrollToId';\r\nimport { NumberOfReviews } from '../../components/Product';\r\n\r\ninterface AverageStarRatingProps {\r\n businessUnitId: string | null;\r\n averageStarRating: number | null;\r\n numberOfReviews: NumberOfReviews;\r\n hasPositiveNumberReviews: boolean;\r\n}\r\n\r\nexport default function AverageStarRating({\r\n averageStarRating,\r\n numberOfReviews,\r\n hasPositiveNumberReviews,\r\n}: AverageStarRatingProps) {\r\n\r\n function renderTrustPilot(averageStarRating: number | null) {\r\n let label = '';\r\n let cssModifier = '';\r\n\r\n switch (averageStarRating) {\r\n case 1:\r\n label = 'one star rating';\r\n cssModifier = 'one';\r\n break;\r\n case 1.5:\r\n label = 'one and a half star rating';\r\n cssModifier = 'one-point-five';\r\n break;\r\n case 2:\r\n label = 'two star rating';\r\n cssModifier = 'two';\r\n break;\r\n case 2.5:\r\n label = 'two and a half star rating';\r\n cssModifier = 'two-point-five';\r\n break;\r\n case 3:\r\n label = 'three star rating';\r\n cssModifier = 'three';\r\n break;\r\n case 3.5:\r\n label = 'three and a half star rating';\r\n cssModifier = 'three-point-five';\r\n break;\r\n case 4:\r\n label = 'four star rating';\r\n cssModifier = 'four';\r\n break;\r\n case 4.5:\r\n label = 'four and a half star rating';\r\n cssModifier = 'four-point-five';\r\n break;\r\n case 5:\r\n label = 'five star rating';\r\n cssModifier = 'five';\r\n break;\r\n default:\r\n break;\r\n }\r\n\r\n const reviewCount =\r\n numberOfReviews?.Total && numberOfReviews?.Total > 1\r\n ? `See all ${numberOfReviews.Total} reviews`\r\n : 'See review';\r\n\r\n return (\r\n <>\r\n
\r\n
\r\n \r\n

{label}

\r\n
\r\n
\r\n
\r\n

\r\n {averageStarRating} out of 5\r\n

\r\n
\r\n
\r\n

\r\n <>\r\n {/* Mobile */}\r\n {\r\n e.preventDefault();\r\n scrollToId('reviewsTab1', 'product-tab--selected');\r\n }}\r\n >\r\n {reviewCount}\r\n \r\n\r\n {/* Desktop */}\r\n {\r\n e.preventDefault();\r\n scrollToId('reviewsTab', 'product-tab--selected');\r\n }}\r\n >\r\n {reviewCount}\r\n \r\n \r\n

\r\n
\r\n \r\n );\r\n }\r\n\r\n return (\r\n
\r\n {hasPositiveNumberReviews && renderTrustPilot(averageStarRating)}\r\n
\r\n );\r\n}\r\n","import {useRef, useLayoutEffect } from 'react';\r\nimport canUseDOM from '../../utilities/canUseDOM';\r\n\r\ninterface TrustBoxProps {\r\n businessUnitId?: string | null;\r\n trustBoxTemplateId: string;\r\n trustBoxHeight: string;\r\n productSku?: string | null;\r\n}\r\n\r\ndeclare global {\r\n interface Window {\r\n Trustpilot: {\r\n loadFromElement: Function;\r\n };\r\n }\r\n}\r\n\r\nconst TrustBox = ({\r\n businessUnitId,\r\n trustBoxTemplateId,\r\n trustBoxHeight,\r\n productSku,\r\n}: TrustBoxProps) => {\r\n const ref = useRef(null);\r\n\r\n useLayoutEffect(() => {\r\n if (canUseDOM()) {\r\n window.Trustpilot = window.Trustpilot || {};\r\n if (window.Trustpilot) {\r\n window.Trustpilot.loadFromElement(ref.current, true);\r\n }\r\n }\r\n }, []);\r\n\r\n return (\r\n \r\n \r\n {' '}\r\n Trustpilot\r\n \r\n \r\n );\r\n};\r\nexport default TrustBox;\r\n","const apiBasePath = '/umbraco/surface/TrustPilotSurface/';\r\n\r\nconst fetchBaseConfig: RequestInit = {\r\n credentials: 'same-origin',\r\n headers: {\r\n 'content-type': 'application/json',\r\n accept: 'application/json',\r\n },\r\n};\r\n\r\nexport const getAverageStarRating = (productSku?: string | null) => {\r\n const path = `${apiBasePath}GetProductReviewSummary/?productSku=${productSku}`;\r\n return fetch(path, fetchBaseConfig).then(response => {\r\n return response.json();\r\n });\r\n};\r\n","import { useState } from 'react';\r\n\r\nexport type AccordionProps = {\r\n title: string;\r\n id: string;\r\n ariaControls: string;\r\n ariaLabelledBy: string;\r\n children: React.ReactNode;\r\n expanded?: boolean;\r\n};\r\n\r\nexport default function Accordion({\r\n title,\r\n ariaControls,\r\n ariaLabelledBy,\r\n children,\r\n expanded = false\r\n}: AccordionProps) {\r\n const [isOpen, setIsOpen] = useState(expanded);\r\n\r\n return (\r\n \r\n setIsOpen(!isOpen)}\r\n >\r\n

\r\n \r\n {title}\r\n \r\n

\r\n \r\n setIsOpen(!isOpen)}\r\n >\r\n
{children}
\r\n \r\n \r\n );\r\n}\r\n","import { Component } from \"react\";\r\n\r\nexport interface AlertProps {\r\n className?: string;\r\n type?: 'info' | 'danger' | 'warning' | 'success';\r\n children: React.ReactNode;\r\n // timeout?: number;\r\n // animate?: boolean;\r\n showCloseButton?: boolean;\r\n handleClose?: Function;\r\n size?: 'normal' | 'small';\r\n}\r\n\r\nclass Alert extends Component {\r\n constructor(props: AlertProps) {\r\n super(props);\r\n\r\n this.handleClose = this.handleClose.bind(this);\r\n }\r\n\r\n handleClose(e: React.MouseEvent) {\r\n e.preventDefault();\r\n\r\n if (this.props.handleClose) {\r\n this.props.handleClose();\r\n }\r\n }\r\n\r\n render() {\r\n const {\r\n type = 'info',\r\n className: cssModifiers = '',\r\n showCloseButton = false,\r\n size = 'normal',\r\n } = this.props;\r\n\r\n const typeCss = type ? `alert-${type}` : '';\r\n\r\n return (\r\n \r\n {showCloseButton && (\r\n \r\n ×\r\n \r\n )}\r\n {this.props.children}\r\n \r\n );\r\n }\r\n}\r\n\r\nexport default Alert;\r\n","import classNames from 'classnames';\r\n\r\ninterface ButtonProps {\r\n id?: string;\r\n type?: 'submit' | 'reset' | 'button';\r\n className?: string;\r\n handleClick?: (event: React.MouseEvent) => void;\r\n children: React.ReactNode;\r\n disabled?: boolean;\r\n}\r\n\r\nexport function Button({\r\n id,\r\n type = 'button',\r\n className,\r\n handleClick,\r\n children,\r\n disabled,\r\n}: ButtonProps) {\r\n const btnClasses = classNames('btn', className);\r\n\r\n return (\r\n \r\n {children}\r\n \r\n );\r\n}\r\n","function createMarkup(html: string) {\r\n return { __html: html };\r\n}\r\n\r\ninterface HtmlTextProps {\r\n children: string;\r\n component?: 'span' | 'div';\r\n}\r\n\r\nfunction HtmlText({ component, children }: HtmlTextProps) {\r\n const Component = component || 'span';\r\n return ;\r\n}\r\n\r\nexport default HtmlText;\r\n","interface IconProps {\r\n iconName: string;\r\n size?: number;\r\n modifierCss?: string;\r\n}\r\n\r\nconst Icon = ({ iconName, size, modifierCss = '' }: IconProps) => {\r\n const cssSize = size ? `icon--${size}` : '';\r\n const cacheBuster = global.cacheBuster;\r\n\r\n function createMarkup() {\r\n return {\r\n __html: ``\r\n };\r\n }\r\n\r\n return (\r\n \r\n );\r\n};\r\n\r\nexport default Icon;\r\n","export interface JsonViewProps {\r\n data: any;\r\n}\r\n\r\nexport function JsonView({ data }: JsonViewProps) {\r\n return (\r\n
{JSON.stringify(data, null, 2)}
\r\n );\r\n}\r\n","import { FunctionComponent } from 'react';\r\n\r\ninterface LoaderProps {\r\n isLoading?: boolean;\r\n size?: 'xsmall' | 'small';\r\n style?: React.StyleHTMLAttributes\r\n inlineBlock?: boolean;\r\n cssModifiers?: string;\r\n}\r\n\r\nconst Loader: FunctionComponent = ({ isLoading, size, inlineBlock = false, cssModifiers, style }: LoaderProps) => {\r\n const cssClasses = ['loader'];\r\n\r\n if (size) {\r\n cssClasses.push(`loader--${size}`);\r\n }\r\n if (inlineBlock) {\r\n cssClasses.push('loader--inline-block');\r\n }\r\n\r\n if (cssModifiers) {\r\n cssClasses.push(cssModifiers);\r\n }\r\n\r\n if (isLoading === false) {\r\n return null;\r\n }\r\n\r\n return (\r\n
\r\n );\r\n};\r\n\r\nexport default Loader;","import { FunctionComponent } from 'react';\r\nimport Loader from './Loader';\r\n\r\ninterface LoaderMaskProps {\r\n title?: string;\r\n subtitle?: string;\r\n type?: '' | 'absolute' | 'relative' | 'fixed';\r\n children?: React.ReactNode;\r\n}\r\n\r\nconst LoaderMask: FunctionComponent = ({ title = '', subtitle = '', type = '', children }: LoaderMaskProps) => {\r\n\r\n let loaderType = '';\r\n\r\n switch (type) {\r\n case 'absolute':\r\n loaderType = 'loader-mask--absolute';\r\n break;\r\n case 'relative':\r\n loaderType = 'loader-mask--relative';\r\n break;\r\n case 'fixed':\r\n loaderType = 'loader-mask--fixed';\r\n break;\r\n default:\r\n loaderType = '';\r\n }\r\n\r\n return (\r\n
\r\n {\r\n title &&\r\n
\r\n {title}\r\n
\r\n }\r\n {\r\n children\r\n }\r\n {\r\n !children &&\r\n \r\n }\r\n {\r\n subtitle &&\r\n
\r\n {subtitle}\r\n
\r\n }\r\n
\r\n );\r\n};\r\n\r\nexport default LoaderMask;","/* eslint-disable no-redeclare */\r\n/* eslint-disable @typescript-eslint/no-unused-vars */\r\n\r\nimport { Component } from 'react';\r\nimport { createPortal } from 'react-dom';\r\nimport FocusTrap from 'react-focus-trap';\r\nimport classNames from 'classnames';\r\nimport LoaderMask from './LoaderMask';\r\nimport canUseDOM from '../utilities/canUseDOM';\r\n\r\nconst domAvailable = canUseDOM();\r\n\r\nconst modalRoot = domAvailable ? document.getElementById('app-modals') : null;\r\n\r\ninterface Modal {\r\n el: HTMLDivElement;\r\n}\r\n\r\ninterface ModalProps {\r\n style?: React.CSSProperties;\r\n className?: string;\r\n escapeToClose?: boolean;\r\n isVisible?: boolean;\r\n title?: string;\r\n showClose?: boolean;\r\n handleClose: () => void;\r\n children: React.ReactNode;\r\n footer?: React.ReactNode;\r\n isLoading?: boolean;\r\n}\r\n\r\nclass Modal extends Component {\r\n constructor(props: ModalProps) {\r\n super(props);\r\n if (modalRoot) this.el = document.createElement('div');\r\n this.closeOnEscape = this.closeOnEscape.bind(this);\r\n }\r\n\r\n componentDidMount() {\r\n if (modalRoot) modalRoot.appendChild(this.el);\r\n }\r\n\r\n componentWillUnmount() {\r\n if (modalRoot) modalRoot.removeChild(this.el);\r\n }\r\n\r\n componentDidUpdate(prevProps: ModalProps) {\r\n if (this.props.isVisible !== prevProps.isVisible) {\r\n if (this.props.isVisible) {\r\n document.body.classList.add('modal-open');\r\n } else {\r\n document.body.classList.remove('modal-open');\r\n }\r\n }\r\n }\r\n\r\n closeOnEscape(e?: React.SyntheticEvent) {\r\n const { escapeToClose, handleClose } = this.props;\r\n\r\n if (handleClose && escapeToClose) {\r\n handleClose();\r\n }\r\n }\r\n\r\n render() {\r\n if (!modalRoot) {\r\n return null;\r\n }\r\n\r\n const {\r\n style,\r\n className,\r\n isVisible = false,\r\n title,\r\n showClose,\r\n handleClose,\r\n children,\r\n footer\r\n } = this.props;\r\n\r\n const modalDialogClasses = classNames('modal-dialog', className);\r\n\r\n return createPortal(\r\n <>\r\n \r\n {\r\n e.stopPropagation();\r\n }}\r\n >\r\n \r\n
\r\n {this.props.isLoading && }\r\n {title && (\r\n
\r\n
{title}
\r\n {showClose && handleClose && (\r\n \r\n ×\r\n \r\n )}\r\n
\r\n )}\r\n
{children}
\r\n\r\n {footer &&
{footer}
}\r\n
\r\n
\r\n
\r\n \r\n
\r\n ,\r\n this.el\r\n );\r\n }\r\n}\r\n\r\nexport default Modal;\r\n","import * as React from 'react';\r\nimport { useState, useRef, useEffect, useCallback } from 'react';\r\nimport loadable from '@loadable/component';\r\nimport { RouteComponentProps, navigate, Link } from '@reach/router';\r\nimport {\r\n ProductVariantFacet,\r\n ProductViewModel,\r\n ProductState,\r\n TransactionalCurrency,\r\n ProductFeature,\r\n ProductMediaItemViewModel,\r\n ProductMediaType,\r\n Website,\r\n BasketRowViewModel,\r\n OfferTypes,\r\n Ecommerce\r\n} from '../types/generated';\r\nimport canUseDOM from '../utilities/canUseDOM';\r\nimport { createCurrencyCode } from '../utilities/formatCurrency';\r\nimport StockCtas from './StockCtas';\r\nimport { ProductPageProps } from '../apps/product/ProductPage';\r\nimport { Variants } from '../apps/product/components/Variants';\r\nimport Roundels from './Roundels';\r\nimport Icon from './Icon';\r\nimport Alert from './Alert';\r\nimport { JsonView } from './JsonView';\r\nimport updateQuantityBadge from '../utilities/updateQuantityBadge';\r\nimport { addToBasket, getBasketCatalogueRefs } from '../apps/basket/basketApi';\r\nimport { getAverageStarRating } from '../apps/trustpilot/trustPilotApi';\r\nimport popupAlert from '../utilities/popup-alert';\r\nimport LoaderMask from './LoaderMask';\r\nimport AddToSaveForLater from '../apps/save-for-later/AddToSaveForLater';\r\nimport TrustBox from '../apps/trustpilot/TrustBox';\r\nimport NavLinks from '../apps/product/components/NavLinks';\r\nimport HtmlText from './HtmlText';\r\nimport Accordion from './Accordion';\r\nimport AverageStarRating from '../apps/trustpilot/AverageStarRating';\r\nimport Loader from './Loader';\r\nimport * as basketApi from '../apps/basket/basketApi';\r\nimport Offer from '../apps/Offer/Offer';\r\nimport OfferPrompt from '../apps/Offer/OfferPrompt';\r\nimport {\r\n getProductByProductId,\r\n getProductQuantityInBasket\r\n} from '../apps/product/productPageApi';\r\nimport ProductLessons from '../apps/product/components/ProductLessons';\r\nimport { addGA4ProductEvent } from '../helpers/ga4';\r\n\r\n// Dynamic imports - default\r\nconst ProductMediaGallery = loadable(\r\n () =>\r\n import(\r\n /* webpackPreload: true, webpackChunkName: \"productMediaGallery\" */ './ProductMediaGallery'\r\n )\r\n);\r\n\r\nconst AddToAny = loadable(\r\n () =>\r\n import(\r\n /* webpackChunkName: \"addToAny\" */ '../apps/product/components/AddToAny'\r\n )\r\n);\r\n\r\nconst AddToFavourites = loadable(\r\n () =>\r\n import(\r\n /* webpackChunkName: \"addToFavourites\" */ '../apps/product/components/AddToFavourites'\r\n )\r\n);\r\n\r\nconst AddToWishlist = loadable(\r\n () =>\r\n import(\r\n /* webpackChunkName: \"addToWishlist\" */ '../apps/wishlist/AddToWishlist'\r\n )\r\n);\r\n\r\nconst AddToMyList = loadable(\r\n () =>\r\n import(/* webpackChunkName: \"addToMylist\" */ '../apps/wishlist/AddToMyList')\r\n);\r\n\r\nconst Download = loadable(\r\n () =>\r\n import(\r\n /* webpackChunkName: \"download\" */ '../apps/product/components/Download'\r\n )\r\n);\r\n\r\nconst AlternativeProductPod = loadable(\r\n () =>\r\n import(\r\n /* webpackChunkName: \"alternativeProductPod\" */ './AlternativeProductPod'\r\n )\r\n);\r\n\r\n// Dynamic imports - named\r\nconst ProductPrices = loadable(async () => {\r\n const { ProductPrices } = await import(\r\n /* webpackChunkName: \"productPrices\" */ '../apps/product/components/ProductPrices'\r\n );\r\n return ProductPrices;\r\n});\r\n\r\nconst PriceBreaks = loadable(\r\n () =>\r\n import(\r\n /* webpackChunkName: \"alternativeProductPod\" */ '../apps/product/components/PriceBreaks'\r\n )\r\n);\r\n\r\ntype TabTypes = 'description' | 'reviews';\r\n\r\nexport interface NumberOfReviews {\r\n OneStar?: number | null;\r\n TwoStars?: number | null;\r\n ThreeStars?: number | null;\r\n FourStars?: number | null;\r\n FiveStars?: number | null;\r\n Total?: number | null;\r\n}\r\n\r\ninterface ProductProps extends ProductPageProps, RouteComponentProps {\r\n quantity?: number;\r\n catalogueRefParam?: string;\r\n baseUrl: string;\r\n showProductReviews: boolean;\r\n showDocuments: boolean;\r\n showUsefulLinks: boolean;\r\n trustPilotBusinessUnitId: string | null;\r\n website: Website;\r\n isCssFavouritesEnabled: boolean;\r\n}\r\n\r\nconst Product = ({\r\n quantity: initialQuantity,\r\n isLoggedIn,\r\n product: initialProduct,\r\n transactionalCurrency,\r\n catalogueRefParam = '',\r\n baseUrl,\r\n showSecondaryPrice,\r\n showProductReviews,\r\n showDocuments,\r\n showUsefulLinks,\r\n trustPilotBusinessUnitId,\r\n website,\r\n isCssFavouritesEnabled,\r\n ...props\r\n}: ProductProps) => {\r\n const [minQuantity, setMinQuantity] = useState(\r\n initialProduct &&\r\n !initialProduct.isVariant &&\r\n initialProduct.isUnitPriceDisabled\r\n ? initialProduct.priceBreakMinUnits\r\n : initialQuantity ?? 1\r\n );\r\n\r\n const [quantity, setQuantity] = useState(minQuantity || 1);\r\n\r\n const [product, setProduct] = useState(initialProduct);\r\n const [showMoreOptions, setShowMoreOptions] = useState(props.showMoreOptions);\r\n const [showFeatures, setShowFeatures] = useState(props.showFeatures);\r\n\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [isJustAdded, setIsJustAdded] = useState(false);\r\n const [basketQty, setBasketQty] = useState(0);\r\n const [offerFreeProduct, setOfferFreeProduct] =\r\n useState(null);\r\n // used to determine if page has already loaded\r\n const loadCount = useRef(0);\r\n\r\n const [selectedTab, setSelectedTab] = useState('description');\r\n\r\n // Trustpilot\r\n const [averageStarRating, setAverageStarRating] =\r\n useState(null);\r\n const [hasPositiveNumberReviews, setHasPositiveNumberReviews] =\r\n useState(false);\r\n const [numberOfReviews, setNumberOfReviews] = useState({\r\n OneStar: null,\r\n TwoStars: null,\r\n ThreeStars: null,\r\n FourStars: null,\r\n FiveStars: null,\r\n Total: null\r\n });\r\n\r\n const hasReviews = showProductReviews && hasPositiveNumberReviews;\r\n const hasSecondaryContent = hasReviews;\r\n\r\n function handleQuantityChange(quantity: number) {\r\n setQuantity(quantity);\r\n }\r\n\r\n function handleQuantityOnBlur(quantity: number) {\r\n if (\r\n typeof quantity === 'number' &&\r\n typeof minQuantity === 'number' &&\r\n quantity < minQuantity\r\n ) {\r\n quantity = minQuantity;\r\n if (product?.isUnitPriceDisabled) {\r\n popupAlert.showNegative(\r\n `This product has a minimum order quantity of ${product?.priceBreakMinUnits}.`\r\n );\r\n }\r\n }\r\n setQuantity(quantity);\r\n }\r\n\r\n function handleAddToBasket(e: React.FormEvent) {\r\n e.preventDefault();\r\n if (!product || !product.catalogueRef) return;\r\n\r\n const qty =\r\n typeof quantity === 'string'\r\n ? typeof minQuantity === 'number'\r\n ? minQuantity\r\n : 1\r\n : quantity;\r\n addItemToBasket(product.productId, product.catalogueRef, qty).catch(\r\n (error: Error) => {\r\n console.error(error);\r\n }\r\n );\r\n }\r\n\r\n function handleToggleFavourites(isAdded: boolean) {\r\n if (isAdded) addGA4ProductEvent('add_to_favourites', product!, {});\r\n else addGA4ProductEvent('remove_from_favourites', product!, {});\r\n }\r\n\r\n function handleAddedToWishlist() {\r\n addGA4ProductEvent('add_to_wishlist', product!, {});\r\n }\r\n\r\n function addItemToBasket(\r\n productId: number,\r\n catalogueRef: string,\r\n qty: number\r\n ) {\r\n addGA4ProductEvent(\r\n 'add_to_cart',\r\n product as ProductViewModel,\r\n {\r\n item_list_id: 'pdp',\r\n item_list_name: 'PDP'\r\n } as Ecommerce,\r\n qty\r\n );\r\n\r\n return addToBasket(productId, qty, catalogueRef, false).then(\r\n (response: any) => {\r\n if (response.success && response.warning) {\r\n popupAlert.showWarning(response.message);\r\n } else if (!response.success) {\r\n popupAlert.showNegative(response.message);\r\n } else {\r\n popupAlert.showPositive(response.message);\r\n setIsJustAdded(true);\r\n }\r\n\r\n trackAddToBasket();\r\n trackSqueezelyAddToBasket();\r\n updateQuantityBadge(response.basketItemCount);\r\n getBasketQuantity().then((result: number) => {\r\n setBasketQty(result);\r\n });\r\n }\r\n );\r\n }\r\n\r\n const getBasketQuantity = useCallback(async () => {\r\n let result = 0;\r\n let catalogueRef = product?.catalogueRef;\r\n\r\n if (catalogueRef) {\r\n await getProductQuantityInBasket(catalogueRef).then((data: number) => {\r\n result = data;\r\n });\r\n }\r\n\r\n return result;\r\n }, [product]);\r\n\r\n useEffect(() => {\r\n const item = product;\r\n if (item && item.offerFreeProductID && item.offerFreeProductID > 0) {\r\n getProductByProductId(product?.offerFreeProductID ?? 0).then(\r\n (data: ProductViewModel) => {\r\n setOfferFreeProduct(data);\r\n }\r\n );\r\n }\r\n\r\n addGA4ProductEvent(\r\n 'view_item',\r\n item as ProductViewModel,\r\n {\r\n item_list_id: 'pdp',\r\n item_list_name: 'PDP'\r\n } as Ecommerce\r\n );\r\n }, [product]);\r\n\r\n useEffect(() => {\r\n if (\r\n product &&\r\n product.catalogueRef &&\r\n (product.offerTypeName === OfferTypes.ExtraFree ||\r\n product?.offerTypeName === OfferTypes.FreeProduct)\r\n ) {\r\n getBasketQuantity().then((result: number) => {\r\n setBasketQty(result);\r\n });\r\n }\r\n }, [getBasketQuantity, product]);\r\n /**\r\n * Variants\r\n */\r\n\r\n const variants = product && product.variants;\r\n const facetGroups = variants && variants.facets;\r\n\r\n function getCurrentSelectionIds(facetGroups: ProductVariantFacet[]) {\r\n const selection: number[] = [];\r\n\r\n if (!facetGroups) return selection;\r\n\r\n facetGroups.forEach((facetGroup) => {\r\n if (facetGroup && facetGroup.facetValues) {\r\n facetGroup.facetValues.forEach((facet) => {\r\n if (facet.isSelected) {\r\n selection.push(facet.facetValueId);\r\n }\r\n });\r\n }\r\n });\r\n\r\n return selection;\r\n }\r\n\r\n function trackSqueezelyAddToBasket() {\r\n window._sqzl = window._sqzl || [];\r\n window._sqzl.push({\r\n event: 'AddToCart',\r\n products: [\r\n {\r\n id: product?.catalogueRef,\r\n quantity: quantity\r\n }\r\n ]\r\n });\r\n\r\n console.log('Completed adding product AddToCart event sent to Squeezely');\r\n }\r\n\r\n function trackAddToBasket() {\r\n window.monetateQ = window.monetateQ || [];\r\n window.monetateQ.push(['setPageType', 'product']);\r\n window.monetateQ.push([\r\n 'addProductDetails',\r\n [\r\n {\r\n productId: `${product?.catalogueRef}`,\r\n sku: `${product?.sku}`,\r\n quantity: quantity,\r\n currency: createCurrencyCode(transactionalCurrency)\r\n }\r\n ]\r\n ]);\r\n basketApi\r\n .getBasket()\r\n .then((response) => {\r\n if (response.hasRows) {\r\n response.rows.forEach((element: BasketRowViewModel) => {\r\n window.monetateQ.push([\r\n 'addCartRows',\r\n [\r\n {\r\n productId: element.catalogueRef,\r\n sku: element.sku,\r\n quantity: element.quantity,\r\n unitPrice: element.priceExcVat,\r\n currency: createCurrencyCode(transactionalCurrency)\r\n }\r\n ]\r\n ]);\r\n });\r\n }\r\n window.monetateQ.push(['trackData']);\r\n })\r\n .catch((error) => {\r\n console.log(error);\r\n });\r\n }\r\n\r\n function handleVariantSelection(\r\n facetGroupId: number,\r\n facetId: number | null\r\n ) {\r\n if (facetGroups) {\r\n const newFacetGroups = facetGroups.map((facetGroup) => {\r\n if (facetGroup.id !== facetGroupId) {\r\n return facetGroup;\r\n }\r\n\r\n const fGroup: ProductVariantFacet = {\r\n ...facetGroup,\r\n facetValues:\r\n facetGroup.facetValues &&\r\n facetGroup.facetValues.map((facet) => ({\r\n ...facet,\r\n isSelected: facet.facetValueId === facetId\r\n }))\r\n };\r\n\r\n return fGroup;\r\n });\r\n\r\n if (product && variants) {\r\n const updatedProduct: ProductViewModel = {\r\n ...product,\r\n variants: {\r\n ...variants,\r\n facets: newFacetGroups\r\n }\r\n };\r\n\r\n setProduct(updatedProduct);\r\n\r\n const params = getCurrentSelectionIds(newFacetGroups).join('-');\r\n console.log('navigating to variant');\r\n console.log('baseUrl', baseUrl);\r\n navigate(\r\n `${baseUrl}/${catalogueRefParam.toLowerCase()}${\r\n params ? `?f=${params}` : ``\r\n }`\r\n );\r\n }\r\n }\r\n }\r\n\r\n useEffect(() => {\r\n if (document.activeElement?.getAttribute('name') === 'quantity') return;\r\n const minQty =\r\n product?.isUnitPriceDisabled &&\r\n !product.isVariant &&\r\n product.priceBreakMinUnits > 1\r\n ? product.priceBreakMinUnits\r\n : 1;\r\n if (\r\n product?.isUnitPriceDisabled &&\r\n !product.isVariant &&\r\n typeof quantity === 'number' &&\r\n quantity < minQty\r\n ) {\r\n setQuantity(minQty);\r\n }\r\n\r\n setMinQuantity(minQty);\r\n }, [product, quantity]);\r\n /**\r\n * Get Data\r\n */\r\n\r\n const qsParams = props.location?.search || '';\r\n\r\n useEffect(() => {\r\n // https://stackoverflow.com/questions/53253940/make-react-useeffect-hook-not-run-on-initial-render\r\n if (loadCount.current < 1) {\r\n // this counts the number of times the component has re-rendered and if\r\n // it's less that 1 times it should not get data\r\n // using a ref to prevent re-renders\r\n // Api calls are made to product info and basket ref endpoints\r\n loadCount.current = loadCount.current + 1;\r\n return;\r\n }\r\n\r\n console.log('=== getting data ===');\r\n setIsLoading(true);\r\n\r\n const catalogueRef = catalogueRefParam.toLowerCase();\r\n\r\n console.log(\r\n `/umbraco/surface/ProductsSurface/GetVariants?c=${catalogueRef}${qsParams.replace(\r\n '?',\r\n '&'\r\n )}`\r\n );\r\n\r\n if (canUseDOM() && catalogueRef) {\r\n fetch(\r\n `/umbraco/surface/ProductsSurface/GetVariants?c=${catalogueRef}${qsParams.replace(\r\n '?',\r\n '&'\r\n )}`\r\n )\r\n .then((res) => res.json())\r\n .then((data: ProductState) => {\r\n // console.log('my data', data.product);\r\n if (data && data.product) {\r\n setProduct(data.product);\r\n setShowMoreOptions(data.showMoreOptions);\r\n setShowFeatures(data.showFeatures);\r\n } else {\r\n popupAlert.showNegative(\r\n 'There was a problem loading the product information, please try refreshing the page'\r\n );\r\n }\r\n setIsLoading(false);\r\n })\r\n .catch((err) => {\r\n console.error(err);\r\n popupAlert.showNegative(\r\n 'There was a problem loading the product information, please try refreshing the page'\r\n );\r\n setIsLoading(false);\r\n });\r\n }\r\n }, [catalogueRefParam, qsParams]);\r\n\r\n const [catalogueRefs, setCatalogueRefs] = useState([]);\r\n\r\n useEffect(() => {\r\n if (canUseDOM()) {\r\n getBasketCatalogueRefs().then((refs) => {\r\n setCatalogueRefs(refs);\r\n });\r\n }\r\n }, []);\r\n\r\n function renderUnitOfSale() {\r\n if (\r\n product &&\r\n product.unitOfSale &&\r\n product.unitOfSale !== 'Each' &&\r\n product.unitOfSale !== 'Pack'\r\n ) {\r\n return (\r\n {product.unitOfSale}\r\n );\r\n }\r\n return null;\r\n }\r\n\r\n function renderVariants() {\r\n if (\r\n !product ||\r\n !product.variants\r\n // note: removed this because otherwise the 'more options'\r\n // never shows\r\n // ||\r\n // !product.variants.facets ||\r\n // product.variants.facets.length < 1\r\n ) {\r\n return null;\r\n }\r\n\r\n // if (!product.isVariant && product.variantHeader && !product.variants.variantCatalogRef) {\r\n if (showMoreOptions && product.variantHeader && !product.isRestricted) {\r\n return (\r\n
\r\n \r\n More Options\r\n \r\n
\r\n );\r\n }\r\n\r\n if (product.variantHeader || product.isVariant) {\r\n return (\r\n \r\n );\r\n }\r\n }\r\n\r\n if (!product) {\r\n return (\r\n \r\n There was a problem loading the product. Please try refreshing the page.\r\n \r\n );\r\n }\r\n\r\n const fallbackImage =\r\n 'https://cdn.images.fecom-media.com/image_coming_soon.jpg';\r\n\r\n const prices = () => (\r\n }\r\n product={product}\r\n transactionalCurrency={\r\n transactionalCurrency || TransactionalCurrency.None\r\n }\r\n isAdded={\r\n product.catalogueRef != null\r\n ? catalogueRefs.includes(product.catalogueRef)\r\n : false\r\n }\r\n showSecondaryPrice={showSecondaryPrice}\r\n priceBreak={product.priceBreaks\r\n ?.filter(\r\n (pricebreak) =>\r\n pricebreak.units <= (typeof quantity === 'number' ? quantity : 1)\r\n )\r\n .slice(-1)\r\n .pop()}\r\n website={website}\r\n />\r\n );\r\n\r\n const renderCtasAndQuantity = (ariaId: string = '') => (\r\n <>\r\n
\r\n
\r\n {product.isVariant && (\r\n \r\n Select Options\r\n \r\n )}\r\n {!product.isVariant &&\r\n !product.isRestricted &&\r\n !product.isTemporarilyUnavailable && (\r\n \r\n \r\n Quantity:{' '}\r\n \r\n
\r\n {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n if (minQuantity && quantity - 1 < minQuantity) {\r\n return;\r\n }\r\n setQuantity(quantity - 1);\r\n }}\r\n >\r\n -\r\n
\r\n {\r\n const quantity = parseInt(e.target.value, 10);\r\n handleQuantityOnBlur(quantity || minQuantity);\r\n }}\r\n onChange={(e) => {\r\n const quantity = parseInt(e.target.value, 10);\r\n handleQuantityChange(quantity || minQuantity);\r\n }}\r\n value={quantity || ''}\r\n //min={minQuantity}\r\n type=\"number\"\r\n data-hj-whitelist\r\n />\r\n {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n setQuantity(quantity + 1);\r\n }}\r\n >\r\n +\r\n
\r\n
\r\n \r\n \r\n {product.isAdded || isJustAdded\r\n ? 'Added to Basket'\r\n : 'Add to Basket'}\r\n \r\n \r\n \r\n )}\r\n
\r\n {!product.isVariant &&\r\n (product.isRestricted || product.isTemporarilyUnavailable) && (\r\n \r\n {product.isRestricted\r\n ? 'Product is restricted'\r\n : 'Temporarily Unavailable'}\r\n \r\n )}\r\n \r\n {product.stockInfo?.isStockRelevant &&\r\n (product.stockInfo?.isOutOfStock ||\r\n product.stockInfo?.isDelayedStock) && (\r\n \r\n )}\r\n \r\n );\r\n\r\n /* eslint-disable react-hooks/rules-of-hooks */\r\n // Trustpilot\r\n useEffect(() => {\r\n if (showProductReviews) {\r\n getAverageStarRating(product.catalogueRef)\r\n .then((data: any) => {\r\n setAverageStarRating(data?.StarsAverage);\r\n setNumberOfReviews(data?.NumberOfReviews);\r\n setHasPositiveNumberReviews(data?.NumberOfReviews.Total > 0);\r\n })\r\n .catch((err: any) => console.error(err));\r\n }\r\n }, [showProductReviews, product.catalogueRef]);\r\n\r\n // keep in this component for #hash links to scroll page\r\n function renderDescriptionTabsMobile() {\r\n if (product) {\r\n return (\r\n
\r\n
\r\n \r\n {\r\n e.preventDefault();\r\n setSelectedTab('description');\r\n }}\r\n >\r\n Product Description\r\n \r\n \r\n {product.description && selectedTab === 'description' && (\r\n \r\n {product.description}\r\n\r\n
\r\n {product.reasonsToLove && (\r\n \r\n \r\n {product.reasonsToLove}\r\n \r\n \r\n )}\r\n\r\n {product.learningOutcomes && (\r\n \r\n \r\n {product.learningOutcomes}\r\n \r\n \r\n )}\r\n\r\n {product.topTips && (\r\n \r\n {product.topTips}\r\n \r\n )}\r\n
\r\n
\r\n )}\r\n\r\n {hasSecondaryContent && (\r\n <>\r\n {hasReviews && (\r\n <>\r\n \r\n {\r\n e.preventDefault();\r\n setSelectedTab('reviews');\r\n }}\r\n >\r\n Reviews\r\n \r\n \r\n\r\n {selectedTab === 'reviews' && (\r\n \r\n \r\n
\r\n )}\r\n \r\n )}\r\n \r\n )}\r\n \r\n {product.productLessons?.hasProductLessons && (\r\n
\r\n \r\n
\r\n )}\r\n {showFeatures && (\r\n
\r\n

\r\n Further Information\r\n

\r\n
\r\n {product.features &&\r\n product.features.length > 0 &&\r\n product.features.map((feature: ProductFeature) => {\r\n const featureValues: string[] | undefined =\r\n feature.value?.split(',');\r\n return (\r\n \r\n
\r\n {feature.name}\r\n
\r\n
\r\n {featureValues &&\r\n featureValues.map(\r\n (value: string, index: number) => (\r\n
\r\n {value}\r\n {featureValues?.length !== index + 1 && (\r\n
\r\n )}\r\n
\r\n )\r\n )}\r\n
\r\n \r\n );\r\n })}\r\n
\r\n
\r\n )}\r\n\r\n {showDocuments && (\r\n
\r\n

\r\n Documents\r\n

\r\n
    \r\n {product?.documents?.map(\r\n (document: ProductMediaItemViewModel) => (\r\n
  • \r\n \r\n {renderFileTypeIcon(document.mediaType)}{' '}\r\n {document.title}\r\n \r\n
  • \r\n )\r\n )}\r\n
\r\n
\r\n )}\r\n\r\n {showUsefulLinks && (\r\n
\r\n

\r\n Useful Links\r\n

\r\n
    \r\n {product?.links?.map((link: ProductMediaItemViewModel) => (\r\n
  • \r\n \r\n {link.title}\r\n \r\n
  • \r\n ))}\r\n
\r\n
\r\n )}\r\n\r\n
\r\n {transactionalCurrency !== null && (\r\n
\r\n }\r\n alternativeProducts={product.alternativeProducts}\r\n transactionalCurrency={transactionalCurrency}\r\n />\r\n
\r\n )}\r\n
\r\n \r\n );\r\n }\r\n }\r\n\r\n // keep in this component for #hash links to scroll page\r\n function renderDescriptionTabsDesktop() {\r\n if (product) {\r\n return (\r\n
\r\n
\r\n
\r\n \r\n {\r\n e.preventDefault();\r\n setSelectedTab('description');\r\n }}\r\n >\r\n Product Description\r\n \r\n \r\n\r\n {hasSecondaryContent && (\r\n <>\r\n {hasReviews && (\r\n <>\r\n |\r\n \r\n {\r\n e.preventDefault();\r\n setSelectedTab('reviews');\r\n }}\r\n >\r\n Reviews\r\n \r\n \r\n \r\n )}\r\n \r\n )}\r\n
\r\n\r\n {product.description && selectedTab === 'description' && (\r\n \r\n {product.description}\r\n\r\n
\r\n {product.reasonsToLove && (\r\n \r\n \r\n {product.reasonsToLove}\r\n \r\n \r\n )}\r\n\r\n {product.learningOutcomes && (\r\n \r\n \r\n {product.learningOutcomes}\r\n \r\n \r\n )}\r\n\r\n {product.topTips && (\r\n \r\n {product.topTips}\r\n \r\n )}\r\n
\r\n
\r\n )}\r\n {/* end description panel */}\r\n\r\n {selectedTab === 'reviews' && (\r\n \r\n \r\n
\r\n )}\r\n \r\n \r\n );\r\n }\r\n }\r\n\r\n function renderFileTypeIcon(mediaType: ProductMediaType) {\r\n let iconName;\r\n switch (mediaType) {\r\n case ProductMediaType.pdf:\r\n iconName = 'file-pdf-o';\r\n break;\r\n case ProductMediaType.doc:\r\n case ProductMediaType.docx:\r\n case ProductMediaType.odt:\r\n case ProductMediaType.txt:\r\n iconName = 'file-word-o';\r\n break;\r\n case ProductMediaType.xls:\r\n case ProductMediaType.xslx:\r\n case ProductMediaType.ods:\r\n iconName = 'file-excel-o';\r\n break;\r\n case ProductMediaType.ppt:\r\n case ProductMediaType.pptx:\r\n case ProductMediaType.odp:\r\n iconName = 'file-powerpoint-o';\r\n break;\r\n default:\r\n return;\r\n }\r\n return ;\r\n }\r\n\r\n function renderPriceBreaks() {\r\n if (product) {\r\n const priceBreaks =\r\n product.isUnitPriceDisabled && product.priceBreaks\r\n ? product.priceBreaks.slice(1)\r\n : product.priceBreaks;\r\n\r\n if (priceBreaks && priceBreaks.length > 0) {\r\n return (\r\n
\r\n }\r\n priceBreaks={priceBreaks}\r\n transactionalCurrency={transactionalCurrency}\r\n website={website}\r\n showSecondaryPrice={showSecondaryPrice}\r\n updateQuantity={handleQuantityChange}\r\n />\r\n
\r\n );\r\n }\r\n }\r\n }\r\n\r\n return (\r\n
\r\n {isLoading && }\r\n

\r\n {product.name} {renderUnitOfSale()}\r\n

\r\n\r\n {/* Mobile */}\r\n {hasReviews && (\r\n
\r\n \r\n
\r\n )}\r\n\r\n
\r\n
\r\n
\r\n
\r\n \r\n \r\n
\r\n\r\n {isLoading &&
}\r\n {!isLoading && (\r\n <>\r\n {/* Mobile */}\r\n
\r\n \r\n
\r\n {/* Desktop */}\r\n
\r\n \r\n
\r\n \r\n )}\r\n
\r\n {product.isDiscountExcluded && (\r\n \r\n \r\n This product is not eligible for any further discount\r\n \r\n
\r\n )}\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n

\r\n {product.name} {renderUnitOfSale()}\r\n

\r\n\r\n {!product.isVariant && (\r\n <>\r\n
\r\n
\r\n {product.catalogueRef}\r\n
\r\n
\r\n
\r\n
\r\n

Product code: {product.catalogueRef}

\r\n
\r\n
\r\n \r\n )}\r\n\r\n {/* Tablet/Desktop */}\r\n {hasReviews && (\r\n
\r\n \r\n
\r\n )}\r\n\r\n
\r\n \r\n
\r\n\r\n {/* Mobile/Tablet */}\r\n
\r\n {product.directDelivery && (\r\n
\r\n \r\n \r\n {product.directDeliveryText}\r\n \r\n
\r\n )}\r\n {renderVariants()}\r\n {prices()}\r\n\r\n
\r\n \r\n
\r\n\r\n
\r\n {\r\n if (!product || !product.catalogueRef) return;\r\n addItemToBasket(0, catalogueRef, quantity);\r\n }}\r\n className=\"offer-prompt\"\r\n offerFreeProduct={offerFreeProduct}\r\n />\r\n
\r\n\r\n {renderCtasAndQuantity('1')}\r\n
\r\n {!product.isRestricted && !product.isVariant && (\r\n <>\r\n {isCssFavouritesEnabled ? (\r\n \r\n \r\n
\r\n ) : (\r\n <>\r\n }\r\n catalogueRef={product.catalogueRef}\r\n isLoggedIn={isLoggedIn}\r\n handleToggleFavourites={handleToggleFavourites}\r\n />\r\n {isLoggedIn ? (\r\n \r\n ) : (\r\n \r\n )}\r\n \r\n )}\r\n {/* todo flag isIntl */}\r\n \r\n \r\n )}\r\n
\r\n {product.hasPriceBreak && renderPriceBreaks()}\r\n {renderDescriptionTabsMobile()}\r\n
\r\n {/* end Mobile/Tablet */}\r\n\r\n {/* Desktop */}\r\n
\r\n {prices()}\r\n {renderVariants()}\r\n
\r\n\r\n
\r\n \r\n
\r\n\r\n
\r\n {\r\n if (!product || !product.catalogueRef) return;\r\n addItemToBasket(0, catalogueRef, quantity);\r\n }}\r\n className=\"offer-prompt\"\r\n offerFreeProduct={offerFreeProduct}\r\n />\r\n
\r\n {variants?.hasNoProductForCombination && (\r\n
\r\n

\r\n No product is currently available for this combination\r\n

\r\n
\r\n )}\r\n\r\n
\r\n {renderCtasAndQuantity()}\r\n {product.directDelivery && (\r\n
\r\n \r\n \r\n \r\n \r\n {product.directDeliveryText}\r\n \r\n
\r\n )}\r\n\r\n
\r\n {!product.isRestricted && !product.isVariant && (\r\n <>\r\n {isCssFavouritesEnabled ? (\r\n \r\n \r\n
\r\n ) : (\r\n <>\r\n }\r\n catalogueRef={product.catalogueRef}\r\n isLoggedIn={isLoggedIn}\r\n handleToggleFavourites={handleToggleFavourites}\r\n />\r\n {isLoggedIn ? (\r\n \r\n ) : (\r\n \r\n )}\r\n \r\n )}\r\n\r\n } />\r\n \r\n )}\r\n
\r\n {product.hasPriceBreak && renderPriceBreaks()}\r\n {!product.isRestricted && !product.isVariant && (\r\n
\r\n {/* todo flag isIntl */}\r\n \r\n
\r\n )}\r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n \r\n\r\n
\r\n
\r\n
{renderDescriptionTabsDesktop()}
\r\n
\r\n {product.productLessons?.hasProductLessons && (\r\n \r\n \r\n
\r\n )}\r\n {showFeatures && (\r\n
\r\n

\r\n Further Information\r\n

\r\n
\r\n {product.features &&\r\n product.features.length > 0 &&\r\n product.features.map((feature: ProductFeature) => {\r\n const featureValues: string[] | undefined =\r\n feature.value?.split(',');\r\n return (\r\n \r\n
\r\n {feature.name}\r\n
\r\n
\r\n {featureValues &&\r\n featureValues.map(\r\n (value: string, index: number) => (\r\n
\r\n {value}\r\n {featureValues?.length !== index + 1 && (\r\n
\r\n )}\r\n
\r\n )\r\n )}\r\n
\r\n
\r\n );\r\n })}\r\n \r\n \r\n )}\r\n\r\n {showDocuments && (\r\n
\r\n

\r\n Documents\r\n

\r\n
    \r\n {product?.documents?.map(\r\n (document: ProductMediaItemViewModel) => (\r\n
  • \r\n \r\n {renderFileTypeIcon(document.mediaType)}{' '}\r\n {document.title}\r\n \r\n
  • \r\n )\r\n )}\r\n
\r\n
\r\n )}\r\n\r\n {showUsefulLinks && (\r\n
\r\n

\r\n Useful Links\r\n

\r\n
    \r\n {product?.links?.map((link: ProductMediaItemViewModel) => (\r\n
  • \r\n \r\n {link.title}\r\n \r\n
  • \r\n ))}\r\n
\r\n
\r\n )}\r\n\r\n
\r\n {transactionalCurrency !== null && (\r\n
\r\n }\r\n alternativeProducts={product.alternativeProducts}\r\n transactionalCurrency={transactionalCurrency}\r\n />\r\n
\r\n )}\r\n
\r\n
\r\n \r\n \r\n {false && (\r\n \r\n )}\r\n
\r\n );\r\n};\r\n\r\nexport default Product;\r\n","import { Roundel as ProductRoundel } from '../types/generated';\r\nexport interface RoundelsProps {\r\n roundel: ProductRoundel | null;\r\n}\r\n\r\nexport default function Roundels({ roundel }: RoundelsProps) {\r\n if (\r\n !roundel?.name ||\r\n roundel?.name === '' ||\r\n (!roundel?.isVendorRoundel &&\r\n (!roundel?.fileName || roundel?.fileName === ''))\r\n ) {\r\n return null;\r\n }\r\n\r\n let cssModifier = '';\r\n\r\n switch (roundel.name.toLowerCase()) {\r\n case 'offer':\r\n cssModifier = 'offer';\r\n break;\r\n case 'new':\r\n cssModifier = 'new';\r\n break;\r\n case 'bestseller':\r\n cssModifier = 'best-seller';\r\n break;\r\n case 'exclusive':\r\n cssModifier = 'exclusive'; // used for other exclusive\r\n break;\r\n case 'vendor':\r\n switch (roundel.text?.toLocaleLowerCase()) {\r\n case 'findel':\r\n cssModifier = 'findel';\r\n break;\r\n case 'clickety books':\r\n cssModifier = 'clickety-books';\r\n break;\r\n case 'cosy direct':\r\n cssModifier = 'cosy';\r\n break;\r\n case 'dryad big book':\r\n cssModifier = 'dryad-big-book';\r\n break;\r\n case 'dryad education':\r\n cssModifier = 'dryad-education';\r\n break;\r\n case 'gam':\r\n cssModifier = 'gam';\r\n break;\r\n case 'heart':\r\n cssModifier = 'heart';\r\n break;\r\n case 'kidzinc':\r\n cssModifier = 'kidzinc';\r\n break;\r\n case 'normans musical instruments':\r\n cssModifier = 'normans-music';\r\n break;\r\n case 'pottery crafts':\r\n cssModifier = 'pottery-crafts';\r\n break;\r\n case 'specialist crafts':\r\n cssModifier = 'specialist-crafts';\r\n break;\r\n case 'surestitch':\r\n cssModifier = 'surestitch';\r\n break;\r\n case 'wildgoose':\r\n cssModifier = 'wildgoose';\r\n break;\r\n default:\r\n break;\r\n }\r\n break;\r\n default:\r\n cssModifier = 'default';\r\n break;\r\n }\r\n\r\n return (\r\n \r\n );\r\n}\r\n\r\ninterface RoundelProps {\r\n name: string | null;\r\n cssModifier: string;\r\n fileName: string | null;\r\n}\r\n\r\nfunction Roundel({ name, cssModifier, fileName }: RoundelProps) {\r\n return fileName && fileName != '' ? (\r\n \r\n {name}\r\n \r\n ) : (\r\n
{name}
\r\n );\r\n}\r\n","type OrigSelect = React.DetailedHTMLProps, HTMLSelectElement>;\r\n\r\nexport interface SelectProps extends Pick> {\r\n id?: string;\r\n label: React.ReactNode;\r\n className?: string;\r\n helpText?: string;\r\n labelClassName?: string;\r\n inputClassName?: string;\r\n style?: React.CSSProperties;\r\n size?: 'sm' | '' | 'lg';\r\n error?: boolean;\r\n required?: boolean;\r\n}\r\n\r\nexport function Select({\r\n id,\r\n size = '',\r\n className = '',\r\n style,\r\n label,\r\n error = false,\r\n required = false,\r\n helpText,\r\n labelClassName = 'col-12',\r\n inputClassName = 'col-12',\r\n children,\r\n ...props\r\n}: SelectProps) {\r\n\r\n const requiredMarker = required ? (\r\n <>\r\n  \r\n *\r\n \r\n) : null;\r\n \r\n const helpId = id ? `${id}-help` : '';\r\n\r\n const helpTextClass = error\r\n ? 'form-text text-danger'\r\n : 'form-text text-muted';\r\n\r\n const helpTextHtml = helpText && (\r\n \r\n {helpText}\r\n );\r\n\r\n return (\r\n
\r\n
\r\n \r\n
\r\n \r\n {children}\r\n \r\n {helpTextHtml}\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","import { useState, useEffect } from 'react';\r\nimport { InStockNotificationRequest } from '../types/generated';\r\nimport OutOfStockForm from '../apps/product/components/OutOfStockForm';\r\nimport { registerForInStockNotification } from '../apps/product/productPageApi';\r\nimport popupAlert from '../utilities/popup-alert';\r\nimport classNames from 'classnames';\r\n\r\ninterface StockCtasProps {\r\n isLoggedIn: boolean;\r\n hasOutOfStockAlternativeProductUrl: boolean;\r\n outOfStockAlternativeProductUrl: string | null;\r\n catalogueRef: string | null;\r\n isOutOfStock?: boolean;\r\n isDelayedStock?: boolean;\r\n isBackInStockNotifyEnabled?: boolean;\r\n}\r\n\r\nfunction StockCtas({\r\n isLoggedIn,\r\n hasOutOfStockAlternativeProductUrl,\r\n outOfStockAlternativeProductUrl,\r\n catalogueRef,\r\n isOutOfStock,\r\n isDelayedStock,\r\n isBackInStockNotifyEnabled\r\n}: StockCtasProps) {\r\n const [showModal, setShowModal] = useState(false);\r\n\r\n function handleModalOpen() {\r\n event?.preventDefault();\r\n setShowModal(true);\r\n }\r\n\r\n function handleModalClose() {\r\n setShowModal(false);\r\n }\r\n\r\n useEffect(() => {\r\n function updateDataLayer() {\r\n (window as any).dataLayer = (window as any).dataLayer || [];\r\n\r\n (window as any).dataLayer.push({\r\n event: 'outOfStockAlternativeLink',\r\n catalogueRef\r\n });\r\n }\r\n\r\n if (hasOutOfStockAlternativeProductUrl) {\r\n updateDataLayer();\r\n }\r\n }, [catalogueRef, hasOutOfStockAlternativeProductUrl]);\r\n\r\n function handleSubmit() {\r\n const form: Partial = {\r\n catalogueRef\r\n };\r\n\r\n registerForInStockNotification(form)\r\n .then((data) => {\r\n if (data.success) {\r\n // if change successful\r\n popupAlert.showPositive(\r\n `Your request has been successful. We will send an email to ${data.emailAddress} to let you know when this item is back in stock.`\r\n );\r\n } else {\r\n // if change unsuccessful\r\n popupAlert.showNegative(\r\n 'There was a problem submitting your details, please try again'\r\n );\r\n }\r\n })\r\n .catch((err: Error) => {\r\n popupAlert.showNegative(\r\n 'There was a problem submitting your details, please try again'\r\n );\r\n console.error(err.message);\r\n });\r\n }\r\n\r\n function handleNotifications() {\r\n if (isLoggedIn) {\r\n handleSubmit();\r\n } else {\r\n handleModalOpen();\r\n }\r\n }\r\n\r\n const outOfStockOrDelayed =\r\n (isOutOfStock || isDelayedStock) &&\r\n isBackInStockNotifyEnabled &&\r\n !hasOutOfStockAlternativeProductUrl;\r\n\r\n const outOfStockAlternative =\r\n (!isOutOfStock || !isDelayedStock) &&\r\n !isBackInStockNotifyEnabled &&\r\n hasOutOfStockAlternativeProductUrl;\r\n\r\n const oneButtonClass = classNames({\r\n 'stock-indicator-ctas--single': outOfStockOrDelayed || outOfStockAlternative\r\n });\r\n\r\n return (\r\n <>\r\n {(isOutOfStock ||\r\n isDelayedStock ||\r\n hasOutOfStockAlternativeProductUrl) && (\r\n
\r\n {(isOutOfStock || isDelayedStock) && isBackInStockNotifyEnabled && (\r\n <>\r\n \r\n Notify me when in stock\r\n \r\n \r\n \r\n )}\r\n {hasOutOfStockAlternativeProductUrl && (\r\n \r\n View Alternative\r\n \r\n )}\r\n
\r\n )}\r\n \r\n );\r\n}\r\n\r\nexport default StockCtas;\r\n","import { RefObject } from 'react';\r\n\r\ntype columnCount = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;\r\ntype columnSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\r\n\r\nexport type Columns = { [key in columnSize]?: columnCount };\r\n\r\nconst getCols = (cols?: Columns): string => {\r\n if (!cols) return '';\r\n\r\n const css = Object.keys(cols).map((key) => {\r\n return `col-${key !== 'xs' ? key + '-' : ''}${cols[key as columnSize]}`;\r\n });\r\n\r\n return css.join(' ');\r\n};\r\n\r\ninterface TextInputProps {\r\n label?: string;\r\n type?: string;\r\n helpText?: React.ReactNode;\r\n name: string;\r\n id?: string;\r\n required?: boolean;\r\n disabled?: boolean;\r\n error?: boolean;\r\n autoFocus?: boolean;\r\n placeholder?: string;\r\n value?: string | number;\r\n onChange?(e: React.ChangeEvent): void;\r\n onBlur?(e: React.FocusEvent): void;\r\n minLength?: number;\r\n maxLength?: number;\r\n inputCols?: Columns;\r\n labelCols?: Columns;\r\n className?: string;\r\n disableAutoComplete?: boolean;\r\n refObject?: RefObject;\r\n defaultValue?: string | number;\r\n}\r\n\r\nconst TextInput = ({\r\n label = '',\r\n type = 'text',\r\n helpText = '',\r\n name = '',\r\n id = '',\r\n required = false,\r\n disabled = false,\r\n error = false,\r\n autoFocus = false,\r\n placeholder = '',\r\n value,\r\n onChange,\r\n onBlur,\r\n minLength,\r\n maxLength,\r\n inputCols,\r\n labelCols,\r\n className = '',\r\n disableAutoComplete = false,\r\n refObject,\r\n defaultValue\r\n}: TextInputProps) => {\r\n const hasColumns = inputCols && labelCols;\r\n\r\n const helpId = id ? `${id}-help` : '';\r\n\r\n const labelColCss = getCols(labelCols);\r\n const inputColCss = getCols(inputCols);\r\n\r\n const requiredMarker = required ? (\r\n <>\r\n  \r\n *\r\n \r\n ) : null;\r\n\r\n const helpTextClass = error\r\n ? 'form-text text-danger'\r\n : 'form-text text-muted';\r\n\r\n const labelHtml = label && (\r\n \r\n {label}\r\n {requiredMarker}\r\n \r\n );\r\n\r\n const input = (\r\n \r\n );\r\n\r\n const helpTextHtml = helpText && (\r\n \r\n {helpText}\r\n \r\n );\r\n\r\n return (\r\n \r\n {hasColumns ?
{labelHtml}
: labelHtml}\r\n\r\n {hasColumns ? (\r\n
\r\n {input}\r\n {helpTextHtml}\r\n
\r\n ) : (\r\n input\r\n )}\r\n\r\n {!hasColumns && helpTextHtml}\r\n \r\n );\r\n};\r\n\r\nexport default TextInput;\r\n","\r\nimport canUseDOM from '../utilities/canUseDOM';\r\n\r\nexport const updateSavedItemsCount = (quantity: number) => {\r\n if (canUseDOM()) {\r\n const savedForLaterCounter = document.querySelector(\r\n '.header-icon__badge--saved-for-later'\r\n );\r\n\r\n if (savedForLaterCounter) {\r\n savedForLaterCounter.innerText = quantity.toString();\r\n }\r\n }\r\n}","function canUseDOM() {\r\n return !!(\r\n typeof window !== 'undefined' &&\r\n window.document &&\r\n window.document.createElement\r\n );\r\n}\r\n\r\nexport default canUseDOM;\r\n","import { TransactionalCurrency } from '../types/enums';\r\n\r\nconst formatCurrency = (\r\n amount: number,\r\n currencyEnum: TransactionalCurrency = TransactionalCurrency.GBP\r\n) => {\r\n let currency = 'GBP';\r\n\r\n switch (currencyEnum) {\r\n case TransactionalCurrency.EUR:\r\n currency = 'EUR';\r\n break;\r\n case TransactionalCurrency.GBP:\r\n default:\r\n currency = 'GBP';\r\n break;\r\n case TransactionalCurrency.AED:\r\n currency = 'AED';\r\n break;\r\n }\r\n\r\n // numeral(amount).format('0.00');\r\n return amount?.toLocaleString(undefined, {\r\n style: 'currency',\r\n currency\r\n });\r\n};\r\nexport default formatCurrency;\r\n\r\nexport function createCurrencyCode(currencyEnum: TransactionalCurrency) {\r\n switch (currencyEnum) {\r\n case TransactionalCurrency.GBP:\r\n return 'GBP';\r\n case TransactionalCurrency.EUR:\r\n return 'EUR';\r\n case TransactionalCurrency.AED:\r\n return 'AED';\r\n default:\r\n return 'GBP';\r\n }\r\n}\r\n","import canUseDOM from './canUseDOM';\r\n\r\n// Scroll to the Product Description and Review Tabs\r\nexport function scrollToId(elementId: string, selectedClass: string) {\r\n if (canUseDOM()) {\r\n const element = document.getElementById(elementId);\r\n\r\n if (element) {\r\n element.click();\r\n element.classList.add(selectedClass);\r\n element.scrollIntoView({\r\n block: 'center',\r\n behavior: 'smooth'\r\n });\r\n }\r\n }\r\n}\r\n\r\n// Scroll to the Description tabbed content from still having the Reviews Tab selected\r\n// Once Description tab is selected, scroll to ReasonsToLove, Learning Outcomes, TopTips depending on which one is clicked\r\nexport function scrollToNestedId(\r\n elementId: string,\r\n selectedClass: string,\r\n nestedElementId: string\r\n) {\r\n if (canUseDOM()) {\r\n const element = document.getElementById(elementId);\r\n const nestedElement = document.getElementById(nestedElementId);\r\n\r\n if (element) {\r\n element.click();\r\n element.classList.add(selectedClass);\r\n }\r\n\r\n if (nestedElement) {\r\n nestedElement.scrollIntoView({\r\n block: 'center',\r\n behavior: 'smooth'\r\n });\r\n }\r\n }\r\n}\r\n","function updateQuantityBadge(quantity: number) {\r\n if (document) {\r\n const basketCounters = document.querySelectorAll(\r\n '.js-header-icon-link .header-icon__badge',\r\n );\r\n\r\n if (basketCounters) {\r\n const counters = Array.from(basketCounters);\r\n counters.forEach(basketLovelyCounter => {\r\n basketLovelyCounter.innerText = quantity.toString();\r\n });\r\n }\r\n }\r\n}\r\n\r\nexport default updateQuantityBadge;\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AAEA;;;AAGA;AAMA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAOA;AACA;AAMA;AACA;AACA;AASA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAUA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AAIA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AA/BA;AAAA;AAuCA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AAIA;AACA;AAAA;AAGA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AA1BA;AAAA;AAxCA;AAyEA;;;;;;;;;;;;;;;;;;;;;;;AC1MA;AACA;AACA;AACA;;;AAIA;AAcA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AACA;AAEA;AAGA;AACA;AACA;AADA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAhBA;AAmBA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAfA;AAiBA;AAAA;AAlBA;AAqBA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAAA;AADA;AADA;;;;;;;;;;;;ACtFA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;;;;AASA;AAKA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AANA;AASA;AAAA;AAAA;AATA;AAaA;AACA;AACA;AACA;AACA;AACA;AANA;AAUA;AACA;AAFA;AATA;AAkBA;AACA;AACA;AACA;AACA;AAKA;AAVA;AAcA;AACA;AAFA;AAbA;AAsBA;AACA;AACA;AACA;AACA;AAKA;AAVA;AAcA;AACA;AAFA;AAbA;AAsBA;AACA;AACA;AACA;AACA;AAKA;AAVA;AAcA;AACA;AAFA;AAbA;AAxEA;AA6FA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7GA;AASA;AAEA;AACA;AACA;AACA;AACA;AACA;;;;AAIA;AACA;AACA;AACA;AAHA;AAYA;AAIA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAJA;AACA;AAMA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAJA;AAOA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAXA;AAaA;AAEA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AAHA;AAKA;AAAA;AALA;AAOA;AAAA;AAKA;AAAA;AACA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAVA;AAJA;AADA;AAoBA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAKA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AATA;AAJA;AADA;AAmBA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAKA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AATA;AAJA;AADA;AAmBA;AAAA;AACA;AAAA;AAEA;AACA;AACA;AAHA;AAAA;AAOA;AAAA;AAAA;AAAA;AARA;AADA;AA3DA;AAbA;AAJA;AAbA;AANA;AAoHA;;;;;;;;;;;;;;;;;;;;;;;;AC9LA;;;;AAQA;AAEA;AACA;AADA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AAAA;AADA;AAKA;AAAA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAJA;AAMA;AAAA;AAEA;AACA;AACA;AAHA;AADA;AAQA;AACA;AAFA;AAAA;AAMA;AAAA;AACA;AAAA;AADA;AAnBA;AADA;AADA;AA2BA;AAAA;AA5BA;AANA;AADA;AAwCA;AACA;AAKA;AAAA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AAJA;AAAA;AASA;AAAA;AAAA;AAXA;AAgBA;AACA;AAKA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AADA;AADA;AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACrHA;;;AAQA;AAAA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAZA;AAcA;AAAA;AACA;AAGA;AAFA;AAAA;AADA;AAfA;AAyBA;AAIA;AAAA;AACA;AAAA;AAAA;AADA;AAtCA;AA8CA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC5IA;AACA;AACA;AAKA;AACA;AACA;AAEA;;;;AAUA;AAKA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AAAA;AACA;AAAA;AADA;AAIA;AACA;AAAA;AAGA;AACA;AAFA;AAIA;AAAA;AACA;AAAA;AAAA;AADA;AAJA;AAWA;AACA;AAFA;AAIA;AAAA;AACA;AAAA;AAAA;AADA;AAJA;AAZA;AAwBA;AAAA;AAGA;AACA;AAFA;AAIA;AAAA;AACA;AAAA;AAAA;AADA;AAJA;AAWA;AACA;AAFA;AAIA;AAAA;AACA;AAAA;AAAA;AADA;AAJA;AAZA;AAzBA;AANA;AA0DA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/JA;AAEA;AACA;AACA;AACA;AACA;AAFA;AAFA;AAQA;AAKA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAHA;AAHA;AAQA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AADA;AAHA;AAMA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AADA;AAHA;AAMA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAFA;AAHA;AAOA;AAAA;AACA;AAEA;AACA;AAEA;AAEA;AAFA;AAGA;AAAA;AACA;;;;;;;;;;;;ACxEA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;AAUA;AAIA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AAtCA;AACA;AAwCA;AAKA;AACA;AACA;AAAA;AACA;AAAA;AAEA;AADA;AAGA;AAAA;AAAA;AAHA;AADA;AAOA;AAAA;AACA;AACA;AAAA;AADA;AADA;AAKA;AAAA;AACA;AAAA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AANA;AAQA;AAAA;AARA;AAaA;AACA;AACA;AACA;AACA;AACA;AANA;AAQA;AAAA;AARA;AAdA;AADA;AADA;AAdA;AA6CA;AACA;AACA;AACA;AAAA;AAAA;AAIA;;;;;;;;;;;;ACvHA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;;;;AAiBA;AAKA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAVA;AAaA;AACA;AACA;AAHA;AAAA;AAZA;AAsBA;AACA;AAAA;;;;;;;;;;;;;;;;;;;;AC3DA;AAEA;AACA;AACA;AACA;AACA;AAFA;AAFA;AAQA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACfA;;;AAWA;AAMA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAHA;AAMA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AANA;AAQA;AAAA;AAEA;AACA;AAFA;AAAA;AADA;AARA;AAkBA;AACA;AACA;AACA;AAAA;AAAA;AAJA;AAMA;AAAA;AAAA;AANA;AAtBA;AAgCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACrDA;;;;AAaA;;;;;AACA;AAAA;AACA;AADA;AACA;AAAA;AAEA;AAHA;AAIA;AACA;;;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA;AAEA;AAEA;AAGA;AAJA;AAQA;AACA;AACA;AACA;AAJA;AAMA;AAAA;AAAA;AANA;AAPA;AAmBA;;;;AA7CA;AACA;AA+CA;;;;;;;;;;;;AC7DA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAWA;AAOA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AALA;AAAA;AAUA;;;;;;;;;;;;;;;;;AChCA;AACA;AAAA;AAAA;AACA;AACA;AAMA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;ACRA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AADA;AAGA;AACA;AACA;AAEA;AACA;AAFA;AAKA;AACA;AACA;;;;;;;;;;;;;;;;;;ACpBA;AAAA;AACA;AACA;AAAA;AAAA;AAEA;;;;;;;;;;;;;;;;;;;;ACEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;;;;;;;;;;;;AChCA;AAAA;AAAA;AAAA;AAAA;;;;AASA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AAXA;AACA;AAaA;AACA;AAAA;AAGA;AAAA;AAAA;AASA;AAAA;AAIA;AAAA;AAAA;AAhBA;AAsBA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACrDA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;;;;AAEA;AAEA;AACA;AAkBA;;;;;AACA;AAAA;AACA;AADA;AACA;AAAA;AACA;AACA;AAHA;AAIA;AACA;;;AACA;AACA;AACA;;;AAEA;AACA;AACA;;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA;AAEA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AAJA;AAOA;AACA;AACA;AACA;AACA;AACA;AANA;AAQA;AAAA;AAAA;AACA;AAAA;AACA;AAAA;AAEA;AAAA;AACA;AAAA;AAAA;AAGA;AACA;AACA;AACA;AACA;AALA;AAOA;AAAA;AAAA;AAPA;AAHA;AAeA;AAAA;AAAA;AAEA;AAAA;AAAA;AApBA;AADA;AARA;AANA;AAwCA;AAAA;AAzCA;AA6CA;;;;AAjGA;AACA;AAmGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACnIA;AACA;AACA;AACA;AACA;AAaA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;;;;AAEA;AACA,gyBAEA;AAFA;AAMA;AACA,2NAEA;AAFA;AAMA;AACA,uPAEA;AAFA;AAMA;AACA,+sBAEA;AAFA;AAMA;AACA,8WACA;AADA;AAIA;AACA,qVAEA;AAFA;AAMA;AACA,yOAEA;AAFA;AACA;AAMA;AAAA;AACA;AADA;AAAA;AAAA;AAAA;AAAA;AAAA,6YAEA;AACA;AAHA;AAAA;AACA;AADA;AACA;AADA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA;AACA,2PAEA;AAFA;AACA;AA4BA;AAeA;AACA;AADA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAOA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AANA;AAAA;AAAA;AAAA;AACA;AAQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AACA;AAAA;AACA;AAGA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AAMA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AAIA;AACA;AAFA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAHA;AAAA;AAAA;AAAA;AACA;AADA;AAAA;AAMA;AACA;AACA;AARA;AAAA;AACA;AADA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AAIA;AACA;AAFA;AAKA;AAEA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AAFA;AAHA;AACA;AASA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AAJA;AAQA;AAGA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AALA;AASA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAFA;AAAA;AAJA;AACA;AASA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAFA;AAFA;AACA;AAOA;AAEA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AAKA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAMA;AACA;AAMA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AAAA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AAAA;AAAA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AAPA;AASA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AACA;AAFA;AAAA;AADA;AASA;AACA;AACA;AACA;AAEA;AACA;AAFA;AAKA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAIA;AACA;AACA;AACA;AAEA;AAAA;AACA;AADA;AAEA;AAAA;AAAA;AACA;AACA;AAGA;AAKA;AACA;AAEA;AAAA;AAKA;AAnBA;AADA;AACA;AAuBA;AAAA;AACA;AADA;AAAA;AACA;AACA;AAAA;AACA;AAAA;AAGA;AACA;AAFA;AAAA;AAWA;AACA;AAFA;AAKA;AACA;AAFA;AAAA;AAMA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AATA;AAAA;AAcA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAZA;AAcA;AACA;AAfA;AAkBA;AACA;AACA;AACA;AACA;AACA;AANA;AAAA;AA/BA;AA0CA;AAAA;AAEA;AACA;AAFA;AAAA;AADA;AApDA;AAZA;AAgFA;AACA;AAFA;AAAA;AAhFA;AA8FA;AACA;AAGA;AAGA;AACA;AACA;AACA;AAXA;AA9FA;AADA;AAkHA;AACA;AACA;AACA;AADA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AALA;AAQA;AAGA;AACA;AACA;AACA;AACA;AACA;AATA;AAAA;AAPA;AAuBA;AACA;AACA;AACA;AAJA;AAMA;AAAA;AAAA;AAEA;AAAA;AAGA;AACA;AACA;AACA;AACA;AALA;AAOA;AAAA;AAAA;AAPA;AAeA;AACA;AACA;AACA;AACA;AALA;AAOA;AAAA;AAAA;AAPA;AAeA;AACA;AACA;AACA;AACA;AALA;AAOA;AAAA;AAAA;AAPA;AA9BA;AARA;AAqDA;AAEA;AAEA;AACA;AACA;AACA;AAJA;AASA;AAKA;AACA;AACA;AACA;AACA;AACA;AAXA;AAAA;AARA;AA2BA;AACA;AACA;AACA;AAJA;AAOA;AACA;AACA;AACA;AAJA;AANA;AA3BA;AAFA;AA5EA;AA6HA;AAAA;AACA;AAAA;AADA;AAKA;AAAA;AACA;AAAA;AAAA;AAGA;AAAA;AAGA;AACA;AAAA;AAEA;AAGA;AAFA;AAIA;AAAA;AAAA;AAGA;AAAA;AAGA;AACA;AAAA;AADA;AAHA;AAPA;AAsBA;AA7BA;AAJA;AAuCA;AAAA;AACA;AAAA;AAAA;AAGA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AAHA;AAAA;AADA;AADA;AAFA;AAJA;AAwBA;AAAA;AACA;AAAA;AAAA;AAGA;AAAA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAJA;AAAA;AADA;AADA;AADA;AAJA;AAqBA;AAAA;AAEA;AAAA;AAEA;AAAA;AAAA;AACA;AACA;AAHA;AADA;AAFA;AAvNA;AAoOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AALA;AAQA;AAGA;AACA;AACA;AACA;AACA;AACA;AATA;AAAA;AAPA;AAuBA;AAEA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAJA;AASA;AAKA;AACA;AACA;AACA;AACA;AACA;AAXA;AAAA;AARA;AAFA;AAFA;AAxBA;AA4DA;AACA;AACA;AACA;AAJA;AAMA;AAAA;AAAA;AAEA;AAAA;AAGA;AACA;AACA;AACA;AACA;AALA;AAOA;AAAA;AAAA;AAPA;AAeA;AACA;AACA;AACA;AACA;AALA;AAOA;AAAA;AAAA;AAPA;AAeA;AACA;AACA;AACA;AACA;AALA;AAOA;AAAA;AAAA;AAPA;AA9BA;AARA;AAuDA;AACA;AACA;AACA;AAJA;AAOA;AACA;AACA;AACA;AAJA;AANA;AAlHA;AADA;AAoIA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AArBA;AACA;AAsBA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AAAA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AANA;AADA;AAWA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AAMA;AAAA;AAEA;AACA;AACA;AACA;AAJA;AADA;AAUA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AAFA;AAKA;AAAA;AAAA;AAAA;AAEA;AAEA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAPA;AADA;AAYA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAPA;AADA;AAdA;AARA;AAsCA;AACA;AAFA;AAIA;AAAA;AAJA;AAtCA;AAgDA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AAAA;AAKA;AACA;AAAA;AACA;AAAA;AAAA;AADA;AAKA;AAAA;AACA;AAAA;AACA;AAAA;AADA;AADA;AANA;AAgBA;AAAA;AAEA;AACA;AACA;AACA;AAJA;AADA;AAUA;AAAA;AAEA;AAGA;AACA;AACA;AANA;AADA;AAYA;AAAA;AAEA;AAAA;AAEA;AACA;AACA;AAHA;AAKA;AAAA;AAAA;AANA;AAcA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AARA;AAUA;AACA;AAZA;AADA;AAiBA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AARA;AAUA;AAIA;AACA;AACA;AACA;AACA;AApBA;AADA;AA0BA;AAAA;AAEA;AAGA;AADA;AAMA;AACA;AACA;AAHA;AALA;AAcA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAJA;AAQA;AACA;AACA;AACA;AAJA;AAQA;AACA;AACA;AACA;AAJA;AAfA;AAyBA;AAAA;AAzCA;AAFA;AA3DA;AAgHA;AAAA;AAAA;AAKA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AARA;AAUA;AACA;AAZA;AADA;AAiBA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AARA;AAUA;AAIA;AACA;AACA;AACA;AACA;AApBA;AADA;AAyBA;AAAA;AACA;AAAA;AAAA;AADA;AAOA;AAAA;AAGA;AAAA;AACA;AAEA;AACA;AACA;AAHA;AADA;AAOA;AAAA;AAAA;AARA;AAcA;AAAA;AAEA;AAGA;AADA;AAMA;AACA;AACA;AAHA;AALA;AAcA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAJA;AAQA;AACA;AACA;AACA;AAJA;AAQA;AACA;AACA;AACA;AAJA;AAfA;AAyBA;AAAA;AAAA;AAAA;AAzCA;AAFA;AAiDA;AAAA;AAEA;AAAA;AAFA;AAlEA;AAlNA;AADA;AADA;AADA;AADA;AAjDA;AAsVA;AAAA;AACA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;AAGA;AACA;AAFA;AAIA;AAAA;AAJA;AAQA;AAAA;AACA;AAAA;AAAA;AAGA;AAAA;AAGA;AACA;AAAA;AAEA;AAGA;AAFA;AAIA;AAAA;AAAA;AAGA;AAAA;AAGA;AACA;AAAA;AADA;AAHA;AAPA;AAsBA;AA7BA;AAJA;AAuCA;AAAA;AACA;AAAA;AAAA;AAGA;AAAA;AAEA;AACA;AAAA;AAEA;AACA;AACA;AAHA;AAAA;AADA;AADA;AAFA;AAJA;AAwBA;AAAA;AACA;AAAA;AAAA;AAGA;AAAA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAJA;AAAA;AADA;AADA;AADA;AAJA;AAqBA;AAAA;AAEA;AAAA;AAEA;AAAA;AAAA;AACA;AACA;AAHA;AADA;AAFA;AA9FA;AAFA;AADA,oCAgHA;AAxdA;AAoeA;AACA;AACA;;;;;;;;;;;;;;;;;;;;ACvnDA;AAAA;AACA;AADA;AACA;AAAA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AAzCA;AACA;AA0CA;AACA;AAAA;AACA;AACA;AA5DA;AACA;AA8DA;AAEA;AACA;AACA;AAHA;AAMA;AACA;AAOA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AAFA;AAAA;AAOA;AAAA;AAAA;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3FA;AAaA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAFA;AAMA;AAEA;AACA;AAGA;AACA;AAAA;AAAA;AAAA;AACA;AAGA;AACA;AAAA;AAAA;AACA;AAAA;AACA;AAAA;AAAA;AAAA;AAGA;AAAA;AAGA;AAFA;AAIA;AAJA;AAAA;AADA;AAJA;AADA;AAmBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpEA;AAEA;AACA;AACA;AACA;;;;;AAYA;AAQA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAFA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AADA;AAIA;AAEA;AACA;AACA;AAGA;AACA;AACA;AAGA;AACA;AAEA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAKA;AAKA;AACA;AADA;AAIA;AACA;AAIA;AAAA;AAEA;AAEA;AACA;AAFA;AAAA;AAOA;AACA;AACA;AAHA;AAPA;AAgBA;AACA;AAFA;AAAA;AAjBA;AAJA;AAgCA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACjIA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAyBA;AAsBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAAA;AAAA;AAFA;AAMA;AACA;AAGA;AAEA;AACA;AAFA;AAAA;AACA;AAQA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAjBA;AACA;AAoBA;AACA;AAAA;AAAA;AAAA;AACA;AAIA;AAEA;AADA;AAKA;AAAA;AAAA;AAGA;AAAA;AAAA;AARA;AAmBA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;AC7IA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;;;;;;;;;;;;ACbA;AAAA;AACA;AAKA;AACA;AACA;;;;;;;;;;;;ACRA;AAAA;AAAA;AAAA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAVA;AACA;AACA;AAYA;AACA;AACA;AAFA;AAIA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AARA;AAUA;;;;;;;;;;;;ACxCA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAFA;AAIA;AACA;AACA;AAGA;AACA;AAAA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAFA;AAIA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzCA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;A","sourceRoot":""}