\n );\n};\n\nexport default ImageUpload;\n","import ImageUpload from \"./ImageUpload\";\nexport default ImageUpload;\n","import React from \"react\";\nimport { makeStyles } from \"@material-ui/core/styles\";\nimport InputBase from \"@material-ui/core/InputBase\";\nimport InputLabel from \"@material-ui/core/InputLabel\";\nimport FormControl from \"@material-ui/core/FormControl\";\nimport PropTypes from \"prop-types\";\n\nconst useStyles = makeStyles(\n (theme) => ({\n root: {\n \"label + &\": {\n marginTop: theme.spacing(1.25),\n },\n },\n input: {\n borderRadius: 2,\n position: \"relative\",\n backgroundColor: theme.palette.common.white,\n border: \"1px solid #ced4da\",\n width: \"100%\",\n padding: \"10px 12px\",\n minHeight: 84,\n \"&:focus\": {\n borderColor: theme.palette.primary.main,\n },\n },\n multiline: {\n padding: 0,\n },\n }),\n { name: \"N0PlainTextArea\" },\n);\n\nfunction PlainTextArea({ className, fullWidth, label, ...other }) {\n const classes = useStyles();\n\n return (\n \n {label ? (\n \n {label}\n \n ) : null}\n \n \n );\n}\nPlainTextArea.propTypes = {\n className: PropTypes.string,\n label: PropTypes.string,\n fullWidth: PropTypes.bool,\n};\nexport default PlainTextArea;\n","import React from \"react\";\nimport PropTypes from \"prop-types\";\nimport TextField from \"./TextField\";\nimport { makeStyles } from \"@material-ui/core/styles\";\n\nconst useStyles = makeStyles(\n (theme) => ({\n counter: {\n color: \"#9E9E9E\",\n },\n }),\n { name: \"N0TextFieldWithCharacterCount\" },\n);\n\nfunction TextFieldWithCharacterCount({ value, maxCount, ...props }) {\n const classes = useStyles();\n\n return (\n \n \n {value?.length ?? 0}\n {maxCount > 0 && `/${maxCount}`}\n \n \n }\n maxLength={maxCount}\n {...props}\n />\n );\n}\n\nTextFieldWithCharacterCount.propTypes = {\n maxCount: PropTypes.number,\n ...TextField.propTypes,\n};\n\nTextFieldWithCharacterCount.defaultProps = {\n maxCount: 0,\n};\n\nexport default TextFieldWithCharacterCount;\n","import React, { FunctionComponent } from \"react\";\nimport MuiCheckbox from \"@material-ui/core/Checkbox\";\nimport FormControlLabel from \"@material-ui/core/FormControlLabel\";\nimport { makeStyles } from \"@material-ui/core/styles\";\n\nconst useStyles = makeStyles(\n (theme) => ({\n labelStrong: {\n ...theme.typography.h4,\n },\n }),\n { name: \"N0Checkbox\" },\n);\n\nconst Checkbox = ({ checked, color = \"primary\", labelStrong, ...props }) => {\n const classes = useStyles();\n const classesOverride = {\n label: labelStrong ? classes.labelStrong : null,\n };\n return (\n }\n {...props}\n />\n );\n};\n\nexport default Checkbox;\n","import React, { useMemo } from \"react\";\nimport { KeyboardDatePicker } from \"@material-ui/pickers\";\nimport { makeStyles } from \"@material-ui/core/styles\";\nimport moment from \"moment\";\n\nconst useStyles = makeStyles(\n (theme) => ({\n adornedEnd: {\n paddingRight: 0,\n },\n }),\n { name: \"N0DatePicker\" },\n);\n\nfunction DatePicker({\n disabled,\n fullWidth,\n maxDate,\n minDate,\n onChange,\n value,\n}) {\n const classes = useStyles();\n\n const handleChange = (date, value) => onChange(value);\n\n // value props in DatePicker assumes the input is ParableDate,\n // as MM-DD-YYYY is a non RFC2822/ISO format, it may throw deprecate warning on Firefox v93.0\n // we need to convert back to a Date object\n const format = \"MM-DD-YYYY\";\n const date = useMemo(() => {\n const d = moment(value, format);\n return d.isValid() ? d.toDate() : null; // keep it blank when input is invalid or null\n }, [value, format]);\n\n return (\n \n );\n}\nexport default DatePicker;\n","export default {\n returnsCsrSignInError401:\n \"The current user is not authorized to access this order.\",\n returnsCsrSignInError500:\n \"There was an error with sign in. Please try again.\",\n returnsCsrSignInTitle: \"Customer Service Sign In\",\n returnsCsrSignInDetails:\n \"Enter an Order Number and click Sign In to continue.\",\n returnsCsrSignInOrderPlaceholder: \"Order Number\",\n returnsCsrSignInPrimaryButton: \"Sign in with Narvar\",\n returnsPudoListTitle: \"How would you like to return it?\",\n returnsPudoListItemhomepickupTitle: \"Home Pickup\",\n returnsPudoListItemhomepickupSubtitle: \"Home Pickup\",\n returnsPudoListItemstoreTitle: \"Visit a store\",\n returnsPudoListItemstoreSubtitle: \"No box or label needed\",\n returnsPudoListItemstoreLinktext: \"Find a nearby store\",\n returnsPudoListItemconciergepartnershipTitle: \"Dropoff Partner\",\n returnsPudoListItemconciergepartnershipSubtitle: \"\",\n returnsPudoListItemprinterlessmailTitle: \"Printerless Drop-Off\",\n returnsPudoListItemprinterlessmailSubtitle:\n \"Box your return. No label needed.\",\n returnsPudoListItemmailTitle: \"Ship It Back\",\n returnsPudoListItemmailSubtitle: \"Send your package back to us.\",\n returnsPudoListItemmailLinktext: \"Find a nearby location\",\n returnsPudoListShowall: \"< Back to All Methods\",\n returnsPudoListFeeLabellabelfee: \"Label Fee\",\n returnsPudoListFeeLabelshippingfee: \"Shipping Fee\",\n returnsPudoListFeeLabelpickupfee: \"Pickup Fee\",\n returnsPudoListFeeLabelFree: \"FREE\",\n returnsPudoListSwitchCategory: \"Select different return method\",\n returnsPudoLocationsPostalcodeTitle: \"Choose your location near: \",\n returnsPudoLocationsPostalcodeSearch: \"Search\",\n returnsPudoLocationsPostalcodeCancel: \"Cancel\",\n returnsPudoLocationsDistanceUnitmiles: \"mi\",\n returnsPudoLocationsDistanceUnitkilometers: \"km\",\n returnsPudoLocationsDistanceNounit: \"Directions\",\n returnsPudoPickupTimeslotTitle: \"We can pick up your return\",\n returnsPudoPickupTimeslotDescription:\n \"The pickup window will be from {{earliestHour}} to {{latestHour}}. Hand your package directly to courier or leave your package in a safe place for pickup.\",\n returnsPudoPickupLocationTitle: \"Pickup Location\",\n returnsPudoPickupLocationDescription:\n \"Returns can only be picked up from the original delivery address.\",\n returnsPudoPickupInstructionsToggle: \"Add Pickup Instructions\",\n returnsPudoPickupFeeTitle: \"{{pickupFee}} Pickup Convenience Fee\",\n returnsPudoPickupFeeDescription:\n \"This is a non-refundable fee in addition to any return shipping fees deducted from your return. Once the pickup is scheduled, it cannot be canceled.\",\n returnsPudoPickupInstructionsPlaceholder:\n \"Take the lobby elevator up to the 3rd floor.\",\n returnsPudoPickupInstructionsHandoffTitle:\n \"How will the courier pick up your package?\",\n returnsPudoSubmit: \"Submit\",\n returnsPudoLocationsSortedbyOptionrecommended: \"Recommended\",\n returnsPudoLocationsSortedbyOptiondistance: \"Distance\",\n returnsPudoLocationsSortedbyTitle: \"Sort By\",\n returnsPudoLocationsSkipLink:\n \"or proceed without selecting a drop-off location\",\n returnsPudoMultiLabelsTitle: \"How many shipping labels do you need?\",\n returnsPudoMultiLabelsDescription: \"You’ll need one label per box\",\n returnsPudoMultiLabelsInputPlaceholder: \"Number of labels\",\n returnsPudoMultiLabelsRemarks:\n \"Putting your return items in correctly labeled boxes will help you get your refund faster.\",\n returnsPudoErrorNopudooptions:\n \"No available return methods. Please try again later.\",\n returnsPudoErrorNotimeslots:\n \"No available pickup timeslots. Please go back and choose a different return method.\",\n\n returnsOrderLoginAbove: \"\",\n returnsOrderLoginBelow: \"\",\n returnsOrderLoginFormAbove: \"\",\n returnsOrderLoginFormBelow: \"\",\n returnsOrderLoginSubmitAbove: \"\",\n returnsOrderLoginSubmitBelow: \"\",\n returnsOrderLoginTitleBelow: \"\",\n returnsOrderLoginFieldsAbove: \"\",\n\n returnsOrderLoginTitle: \"Returns\",\n returnsOrderLoginDescription: \"Enter your order information to get started.\",\n returnsOrderLoginFormSubmit: \"Next\",\n returnsOrderLoginFormLabelEmail: \"Email\",\n returnsOrderLoginFormLabelOrder: \"Order Number\",\n returnsOrderLoginFormLabelGiftCode: \"Gift Code\",\n returnsOrderLoginFormCaptionEmail:\n \"We'll use this to send you your return label and refund.\",\n returnsOrderLoginFormCaptionOrder: \"\",\n returnsOrderLoginFormCaptionGiftCode:\n \"Can't find this? Try looking on your gift receipt, or email customer service and we'll help you out.\",\n returnsOrderLoginReturnPolicy: \"Read our Return Policy\",\n returnsOrderLoginError:\n \"We could not find the order. Please confirm information is correct and try again.\",\n returnsOrderLoginFormValidationErrorEmail:\n \"Please enter a valid email address.\",\n returnsOrderLoginToggleGiftReturnTitle: \"Returning a gift?\",\n returnsOrderLoginToggleGiftReturnLinkText: \"Start a gift return\",\n returnsOrderLoginToggleGiftReturnTitle_giftReturn: \"Not returning a gift?\",\n returnsOrderLoginToggleGiftReturnLinkText_giftReturn: \"Start a return\",\n\n returnsReasonsItemInfoTitle: \"{{name}}\",\n returnsReasonsItemInfoColor: \"Color: {{color}}\",\n returnsReasonsItemInfoColorLabel: \"Color:\",\n returnsReasonsItemInfoSize: \"Size: {{size}}\",\n returnsReasonsItemInfoSizeLabel: \"Size:\",\n returnsReasonsItemInfoQuantity: \"Quantity: {{quantity}}\",\n returnsReasonsItemInfoQuantityLabel: \"Quantity:\",\n returnsReasonsItemInfoQuantitySelectPlaceholder: \"Number of Items\",\n returnsReasonsTypeTitle: `What would you like to do?`,\n returnsReasonsTypeReturn: `Return`,\n returnsReasonsTypeExchange: `Exchange`,\n returnsReasonsTypeClaim: `Report shipping issue`,\n returnsReasonsListTitle: `What didn't go as planned?`,\n returnsReasonsListTitleSecondary: \"Specify more detail\",\n returnsReasonsQuantityTitle: \"How many would you like to return?\",\n returnsReasonsQuantityAllTitle:\n \"Is the item unopened and the full quantity being returned?\",\n returnsReasonsQuantityAllTitleHipaa:\n \"Is the item unopened and the full prescription quantity being returned?\",\n returnsReasonsQuantityAllYesLabel: \"Yes\",\n returnsReasonsQuantityAllNoLabel: \"No\",\n returnsReasonsQuantityAllError:\n \"Only unopened items that include their full quantity can be returned.\",\n returnsReasonsPicturesTitle: \"Please upload a photo\",\n returnsReasonsPicturesUploadButton: \"Choose Photo\",\n returnsReasonsPicturesUploadDescription:\n \"Please upload a PNG or JPG up to 10MB\",\n returnsReasonsPicturesUploadErrorMaxSize: \"Images must be smaller than 10MB.\",\n returnsReasonsPicturesUploadErrorMaxCount: \"Please upload 1 or 2 photos.\",\n returnsReasonsCommentsTitle: \"Anything else to share?\",\n returnsReasonsCommentsPlaceholder: \"Leave a comment (optional)\",\n returnsReasonsCommentsPlaceholderRequired: \"Leave a comment (required)\",\n returnsReasonsEditTitle: \"Remove item?\",\n returnsReasonsEditRemoveLabel: \"Yes, remove item\",\n returnsReasonsEditContinueLabel: \"No, continue with return\",\n returnsReasonsNoReasonsError: \"No return reasons available.\",\n returnsReasonsExchangeTitle:\n \"Would you like to exchange for another size or color?\",\n returnsReasonsExchangeUnavailable:\n \"This product is unavailable for exchange.\",\n returnsReasonsExchangeUpsellTitle:\n \"Would you like to exchange for something else?\",\n returnsReasonsExchangeUpsellNoThanks: \"No thanks\",\n returnsReasonsShopNowDescription: \"Not seeing what you want?\",\n returnsReasonsShopNowLink: \"Shop now with credit instead\",\n\n returnsConfirmationstoreActionsPanelHeader: \"RETURN SHIPPING LABEL\",\n returnsConfirmationstoreActionsPanelTitle: `Here's your store return slip`,\n returnsConfirmationstoreActionsPanelTitleHipaa:\n \"Check your email for your return slip\",\n returnsConfirmationstoreActionsPanelMainButton: \"Print Store Return Slip\",\n returnsConfirmationstoreActionsPanelDescription:\n \"A confirmation email has been sent to {{email}}\",\n returnsConfirmationstoreActionsPanelDescriptionHipaa:\n \"A confirmation email has been sent to your email address.\",\n returnsConfirmationstoreActionsPanelEmailButton:\n \"Send to another email address\",\n returnsConfirmationActionsPanelEmailStatusidle: \"\",\n returnsConfirmationActionsPanelEmailStatuserror:\n \"There appears to be an issue sending to the new email address. Please try again later.\",\n returnsConfirmationActionsPanelEmailStatusloading: \"\",\n returnsConfirmationActionsPanelEmailStatussuccess: \"\",\n returnsConfirmationActionsPanelEmailStatusvalidationerror: `There appears to be a problem with this email address. Please check and try again.`,\n returnsConfirmationActionsPanelEmailInputPlaceholder: \"Email\",\n returnsConfirmationstoreInstructionsPanelHeader: \"INSTRUCTIONS\",\n returnsConfirmationstoreStep1Title: `Print your return slip`,\n returnsConfirmationstoreStep1Details: `If you're unable to print, you can show an associate your confirmation email.`,\n returnsConfirmationstoreStep2Title: `Visit our store`,\n returnsConfirmationstoreStep2Details: `Bring along your return items and the return slip. Our sales associate will be happy to help complete your return or exchange.`,\n returnsConfirmationstoreItemsPanelHeader: \"ITEMS TO RETURN\",\n returnsConfirmationmailActionsPanelHeader: \"RETURN SHIPPING LABEL\",\n returnsConfirmationmailActionsPanelTitle: `Here's your {{provider}} return shipping label`,\n returnsConfirmationmailActionsPanelTitleHipaa:\n \"Check your email for your return shipping label\",\n returnsConfirmationmailActionsPanelMainButton: \"Print Return Shipping Label\",\n returnsConfirmationmailActionsPanelDescription:\n \"A confirmation email has been sent to {{email}}\",\n returnsConfirmationmailActionsPanelDescriptionHipaa:\n \"A confirmation email has been sent to your email address.\",\n returnsConfirmationmailActionsPanelEmailButton:\n \"Send to another email address\",\n returnsConfirmationmailInstructionsPanelHeader: \"INSTRUCTIONS\",\n returnsConfirmationmailStep1Title: `Print your return shipping label`,\n returnsConfirmationmailStep2Title: `Pack up your items`,\n returnsConfirmationmailStep2Details: `Place your packing slip and items in the original or new package, and seal it. Mark out any old addresses and barcodes, then attach your return shipping label.`,\n returnsConfirmationmailStep3Title: `Ship your package with {{provider}}`,\n returnsConfirmationmailStep3Details: `You can ship for package to us any way you like, either using a drop box or bringing it to a drop-off location.`,\n returnsConfirmationmailItemsPanelHeader: \"ITEMS TO RETURN\",\n returnsConfirmationprinterless_mailActionsPanelHeader: `MOBILE RETURN CODE`,\n returnsConfirmationprinterless_mailActionsPanelTitle: `Here's your {{provider}} mobile return code`,\n returnsConfirmationprinterless_mailActionsPanelTitleHipaa: `Check your email for your mobile return code`,\n returnsConfirmationprinterless_mailActionsPanelDescription: `This {{provider}} mobile return code was also sent via email to {{email}}`,\n returnsConfirmationprinterless_mailActionsPanelDescriptionHipaa:\n \"Your mobile return code was also sent to your email address.\",\n returnsConfirmationprinterless_mailActionsPanelEmailButton: `Send to another email address`,\n returnsConfirmationprinterless_mailLocationPanelHeader: `DROP-OFF LOCATION`,\n returnsConfirmationprinterless_mailInstructionsPanelHeader: `INSTRUCTIONS`,\n returnsConfirmationprinterless_mailStep1Title: `Pack up your items`,\n returnsConfirmationprinterless_mailStep1Details: `Place your items in the original or new packaging, and seal it. Mark out any old addresses and barcodes.`,\n returnsConfirmationprinterless_mailStep2Title: `Bring your return to {{location}}`,\n returnsConfirmationprinterless_mailStep2TitleHipaa:\n \"Bring your return to your preferred location\",\n returnsConfirmationprinterless_mailStep3Title: `Ship your package with {{provider}}`,\n returnsConfirmationprinterless_mailStep3Details: `You can ship for package to us any way you like, either using a drop box or bringing it to a drop-off location.`,\n returnsConfirmationprinterless_mailItemsPanelHeader: `ITEMS TO RETURN`,\n returnsConfirmationprinterless_mailSecondaryActionsPanelTitle: `Need to print a return label?`,\n returnsConfirmationprinterless_mailSecondaryActionsPanelDescription: `If you change your mind or your mobile return code isn't working, you can always print your label instead.`,\n returnsConfirmationprinterless_mailSecondaryActionsPanelMainButton: `Get label`,\n returnsConfirmationconciergeActionsPanelHeader: \"RETURN SHIPPING LABEL\",\n returnsConfirmationconciergeActionsPanelTitle: `Here's your {{provider}} return shipping label`,\n returnsConfirmationconciergeActionsPanelTitleHipaa:\n \"Check your email for your return shipping label\",\n returnsConfirmationconciergeActionsPanelMainButton:\n \"Print Return Shipping Label\",\n returnsConfirmationconciergeActionsPanelDescription:\n \"A confirmation email has been sent to {{email}}\",\n returnsConfirmationconciergeActionsPanelEmailButton:\n \"Send to another email address\",\n returnsConfirmationconciergeLocationPanelHeader: `DROP-OFF LOCATION`,\n returnsConfirmationconciergeInstructionsPanelHeader: \"INSTRUCTIONS\",\n returnsConfirmationconciergeStep1Title: `Print your return shipping label`,\n returnsConfirmationconciergeStep2Title: `Pack up your items`,\n returnsConfirmationconciergeStep2Details: `Place your packing slip and items in the original or new package, and seal it. Mark out any old addresses and barcodes, then attach your return shipping label.`,\n returnsConfirmationconciergeStep3Title: `Ship your package with {{provider}}`,\n returnsConfirmationconciergeStep3Details: `You can ship for package to us any way you like, either using a drop box or bringing it to a drop-off location.`,\n returnsConfirmationreshopStepTitle:\n 'Download the Reshop app to finish getting your refund.',\n returnsConfirmationreshopStepDetails:\n \"Once you’re set up, we’ll send your money to your bank account\",\n returnsConfirmationreshopStepAppStoreLink:\n \"https://apps.apple.com/us/app/reshop-app/id6457265704\",\n returnsConfirmationreshopStepAppStoreLinkText: \"Download on the App Store\",\n returnsConfirmationreshopStepGooglePlayLink:\n \"https://play.google.com/store/apps/details?id=com.reshopmobile\",\n returnsConfirmationreshopStepGooglePlayLinkText: \"Get it on Google Play\",\n returnsConfirmationconciergeItemsPanelHeader: \"ITEMS TO RETURN\",\n returnsConfirmationError: \"We could not find the return. Try again later.\",\n\n returnsMonikerfedexDisplayName: `FedEx`,\n returnsMonikerfedexLinkText: \"Find a drop-off location\",\n returnsMonikerfedexLinkUrl: \"https://local.fedex.com/\",\n returnsMonikerupsDisplayName: `UPS`,\n returnsMonikerupsLinkText: \"Find a drop-off location\",\n returnsMonikerupsLinkUrl: \"https://www.ups.com/dropoff\",\n returnsMonikeruspsDisplayName: `USPS`,\n\n returnsItemListTitle: \"Hi {{customer}}, what item can we help you with?\",\n returnsItemListTitleHipaa: \"Hello. What would you like to return?\",\n returnsItemListTitleDirty:\n \"Can we help you with anything else, {{customer}}?\",\n returnsItemListTitleDirtyHipaa:\n \"Please review the item to be returned and click Next to proceed. Click the item if you want to make changes.\",\n returnsItemListIneligibleTitle: \"You have no eligible items for return.\",\n returnsItemListEligibleDescription:\n \"These items can be returned until {{returnDate}}\",\n returnsItemListReturnInitiatedTitle: \"Return Initiated\",\n returnsItemListPendingpacking_slip_and_shipping_labelLink:\n \"Reprint Shipping Label\",\n returnsItemListPendingqr_codeLink: \"Get Mobile Return Code\",\n returnsItemListPendingpacking_slipLink: \"Reprint Packing Slip\",\n returnsItemListPendingshipping_labelLink: \"Reprint Shipping Label\",\n returnsItemListPendingCancelReturnLink: \"Cancel Return\",\n returnsItemListIneligibleDescription: \"Not Eligible for Return\",\n returnsItemListSubmit: \"Next\",\n returnsItemListItemInfoTitle: \"{{name}}\",\n returnsItemListItemInfocolorLabel: \"Color: \",\n returnsItemListItemInfoskuLabel: \"SKU: \",\n returnsItemListItemInfoqtyLabel: \"Quantity: \",\n returnsItemListItemInfosizeLabel: \"Size: \",\n returnsItemListItemInfoqtyTransactionSeparator: \" -> \",\n returnsItemListItemInfopriceLabel: \"Price: \",\n returnsItemListItemInforeasonLabel: \"Reason: \",\n returnsItemListItemTagReturning: \"Returning\",\n returnsItemListItemTagExchanging: \"Exchanging\",\n returnsItemListItemTagClaiming: \"Returning\",\n returnsItemListItemTagHasShippingProtection: \"Shipping Protection\",\n returnsItemListItemTagHasShippingProtectionTooltip:\n 'Item is protected if it is delivered damaged, lost during delivery, or stolen. Learn more',\n returnsItemListItemTagExchangingForCredit: \"Exchanging for Credit\",\n returnsItemListNoItemError: \"No valid items in this order to be returned.\",\n returnsItemListBannererror:\n \"Sorry, we are unable to cancel your return at this time. Please try again later. \",\n returnsItemListBannersuccess: \"Your return was successfully cancelled. \",\n returnsLineItemGroupIneligibleDescription:\n \"These items need to be returned to a different location. You can return these items after completing the return for your selected item(s).\",\n returnsLineItemGroupTitle: \"Sold by {{group}}\",\n\n returnsReviewError:\n \"Unfortunately, we are unable to complete your return at this time. Please try again later.\",\n returnsReviewHeader: \"Does everything look right?\",\n returnsReviewDescriptionmail: `You'll be able to download your prepaid shipping label once your return is submitted.`,\n returnsReviewDescriptionconcierge: `You'll be able to download your prepaid shipping label once your return is submitted.`,\n returnsReviewDescriptionprinterless_mail: `You'll receive a Mobile Return Code once your return is submitted.`,\n returnsReviewDescriptionstore: `You will be able to download a return slip to take to our store once you submit your return.`,\n returnsReviewDescriptionself: `You'll be able to download your packing slip once your return is submitted.`,\n returnsReviewSubHeader: \"Return Summary\",\n returnsReviewSubHeaderExchange: \"Exchange Summary\",\n returnsReviewSubHeaderReturnAndExchange: \"Return and Exchange Summary\",\n returnReviewTitleShopNow: \"Shopping Cart\",\n returnsReviewItemCardSize: \"Size\",\n returnsReviewItemCardQuantity: \"Quantity\",\n returnsReviewTotalsSubtotal: \"Subtotal\",\n returnsReviewTotalsShippingFee: \"Return Shipping Fee\",\n returnsReviewTotalsEstimatedRefund: \"Estimated Refund\",\n returnsReviewTotalsEstimatedCharge: \"Total\",\n returnsReviewTotalsHelpText:\n \"* This is an estimate. Actual refund amount is subject to applied discounts, tax, shipping costs, and item condition.\",\n returnsReviewDropOffLocationHeader: \"Drop-Off Location Details\",\n returnsReviewAddEmailHeader: \"Customer Email Address\",\n returnsReviewAddEmailDescription: `Before submitting this return, please include the customer's email address. Return instructions and the shipping label will be sent to this address.`,\n returnsReviewUpdateEmailDescription: `If you need to update the customer's email address, enter it below. Return instructions and the shipping label will be sent to to this new address along with any future notifications on this return.`,\n returnsReviewAddEmailInputLabel: \"Email Address\",\n returnsReviewSubmittingReturn: \"Submitting Return\",\n returnsReviewSubmittingError:\n \"There was a problem processing this return. Try again later.\",\n returnsReviewPolicyCheckbox: \"\",\n returnsReviewRefundTo_originalPayment: \"to {{name}}\",\n returnsReviewRefundTo_giftCard: \"to {{name}}\",\n returnsReviewPreferencesCheckboxLabel:\n 'Save your returns methods & preferences to use for future returns with retailers who use Narvar. You can add, update or delete your preferences at any time. Learn more',\n returnsReviewPreferencesInputLabel:\n \"Confirm your email associated with this order\",\n returnsReviewReshopTitle:\n 'Get Text Updates About Your Refund with Reshop',\n returnsReviewReshopCheckboxLabel:\n \"I want to receive texts from Reshop about my refund\",\n returnsReviewReshopPhoneInputLabel: \"Mobile Number\",\n returnsReviewReshopDisclaimer:\n \"*Assuming we need some sort of disclaimer text here\",\n\n returnsRefundMethodsTitle: \"How would you like to receive your refund?\",\n returnsRefundMethodsDisclaimer: \"\",\n returnsRefundMethodsOptionOriginalPaymentTitle: \"Original Form of Payment\",\n returnsRefundMethodsOptionOriginalPaymentDescription:\n \"Receive a refund to your original form of payment 5-7 days after your return is processed.\",\n returnsRefundMethodsOptionGiftCardTitle: \"Gift Card\",\n returnsRefundMethodsOptionGiftCardDescription:\n \"Receive a gift card to your email after your return is processed.\",\n returnsRefundMethodsOptionReshopTitle:\n 'Instant Refund with Reshop',\n returnsRefundMethodsOptionReshopDescription:\n 'Get your refund in the Reshop app right after submitting your return. Learn more about Reshop',\n returnsRefundMethodsOptionIncentiveChip: \"GET {{amount}}\",\n\n returnsSwatchesListSeeMore: \"See More\",\n returnsSwatchesListSeeLess: \"See Less\",\n\n returnsPreferencesStatusDesc: \"Your return preferences have been applied.\",\n returnsPreferencesStatusDesc_error:\n \"We were unable to save your return preferences since your email address couldn't be verified.\",\n returnsPreferencesStatusDesc_pending:\n \"Your return preferences have been saved. Complete your profile to apply them to future returns.\",\n returnsPreferencesStatusLinkText: \"Manage your preferences\",\n returnsPreferencesStatusLinkText_error: \"Create your profile\",\n returnsPreferencesStatusLinkText_pending: \"Complete profile\",\n\n returnsStepsGeneralError:\n \"There was an error loading the next step. Please try again later.\",\n\n returnsActionsNext: \"Next\",\n returnsActionsBack: \"Back\",\n returnsActionsCancel: \"Cancel\",\n returnsActionsSubmit: \"Submit\",\n returnsActionsBackToLogin: \"Return to Order Login\",\n} as const;\n","import { useCallback } from \"react\";\nimport { useGrowthBook } from \"@growthbook/growthbook-react\";\nimport { isArray, isNil, last, merge } from \"lodash/fp\";\nimport noflake from \"../../shared/modules/noflake\";\nimport { memoObjectByKeyValues } from \"../../shared/modules/object\";\nimport { config } from \"../config\";\n\nexport const PAGE_VIEW_EVENT = \"event.page_view\";\nexport const EXPERIMENT_VIEW_EVENT = \"event.experiment_view\";\nexport const LOGIN_SUCCESS_EVENT = \"event.login_success\";\nexport const SUBMIT_RETURN_SUCCESS_EVENT = \"event.submit_return_success\";\nexport const SAVE_PREFERENCES_CHECKED_EVENT = \"event.save_preferences_checked\";\nexport const MANAGE_PROFILE_CLICKED_EVENT =\n \"action.click.user_preferences_manage_link.choose_method_step\";\nexport const MANAGE_PROFILE_CONFIRMATION_CLICKED_EVENT =\n \"action.click.user_preferences_manage_link.confirmation_step\";\nexport const COMPLETE_PROFILE_CLICKED_EVENT =\n \"action.click.user_preferences_complete_profile_link.confirmation_step\";\n\nexport const countingEvent = (eventType, measureValue = 1) => ({\n eventType,\n measureValue,\n});\n\nconst asyncNoop = async () => {};\n\nexport const convertFromDatadogMetrics = (metrics = [], overrides) =>\n metrics\n .filter((metric) => metric.value > 0)\n .map((metric) => {\n let eventType = last(metric.metricNameSuffix.split(\".\"));\n if (metric.currencyCode) {\n eventType += \"_\" + metric.currencyCode.toLowerCase();\n }\n\n return {\n eventType,\n measureValue: metric.value,\n ...overrides,\n };\n });\n\nconst batchSubmit = (events = []) => {\n if (!isArray(events)) {\n return batchSubmit([events]);\n }\n\n const result = events.map((event) => noflake.log(\"shopify_ab_test\", event));\n return Promise.all(result);\n};\n\nexport const abTestingTrackResult = (\n growthBook,\n events = [],\n testId = null,\n) => {\n if (!growthBook) {\n return Promise.resolve([]);\n }\n // TODO: remove me. for now we won't capture all page view and login events.\n if (!config.isNthUi) {\n return Promise.resolve([]);\n }\n\n const userAttributes = growthBook.getAttributes();\n let variantKey = null;\n if (testId) {\n variantKey = growthBook.getFeatureValue(testId, null);\n if (typeof variantKey !== \"string\" && !isNil(variantKey)) {\n variantKey = JSON.stringify(variantKey);\n }\n }\n\n const patchDefaultValue = merge({\n ...userAttributes,\n testId,\n variantKey,\n eventType: null,\n measureValue: 0,\n });\n\n events = isArray(events) ? events : [events];\n return batchSubmit(events.map(patchDefaultValue));\n};\n\nexport const useAbTesting = () => {\n const growthBook = useGrowthBook();\n\n const trackResult = useCallback(\n (events = [], testId = null) =>\n abTestingTrackResult(growthBook, events, testId),\n [growthBook],\n );\n\n return memoObjectByKeyValues({\n trackResult: growthBook ? trackResult : asyncNoop,\n });\n};\n","import {\n useEffect,\n useLayoutEffect,\n useState,\n useMemo,\n useCallback,\n} from \"react\";\nimport { useSnackbar } from \"notistack\";\nimport { debounce, get, noop } from \"lodash/fp\";\nimport { useElementId } from \"./localId\";\nimport { memoObjectByKeyValues } from \"./object\";\n\nexport function useEffectOnce(fn) {\n return useEffect(fn, []);\n}\n\nexport function useElementSize(element) {\n const [size, setSize] = useState(() => {\n if (element) {\n return {\n width: element.offsetWidth,\n height: element.offsetHeight,\n };\n }\n\n return {\n width: \"auto\",\n height: \"auto\",\n };\n });\n\n useLayoutEffect(() => {\n function updateSize() {\n if (element) {\n setSize({\n width: element.offsetWidth,\n height: element.offsetHeight,\n });\n }\n }\n\n updateSize();\n window.addEventListener(\"resize\", updateSize);\n return () => window.removeEventListener(\"resize\", updateSize);\n }, [element]);\n\n return size;\n}\n\nexport const useModal = (initialValue = false) => {\n const id = useElementId(\"modal-\");\n const [open, setOpen] = useState(initialValue);\n const toggle = () => setOpen((state) => !state);\n const register = useCallback(\n ({ handleCancel, handleConfirm }) => ({\n open,\n onCancel: async () => {\n await handleCancel?.();\n setOpen(false);\n },\n onConfirm: async () => {\n await handleConfirm?.();\n setOpen(false);\n },\n }),\n [id, open],\n );\n return { id, open, setOpen, toggle, register };\n};\n\nexport function useRequestStatus(requestState, customErrorsPath, showRawData) {\n const data = showRawData ? requestState.data : !!requestState.data;\n const customErrors = useMemo(\n () =>\n customErrorsPath ? (get(customErrorsPath, requestState.data) ?? []) : [],\n [customErrorsPath, requestState.data],\n );\n return useMemo(\n () => ({\n data,\n called: requestState.called,\n loading: requestState.loading,\n error: requestState.error ?? customErrors?.[0],\n errors: [].concat(requestState.error ?? []).concat(customErrors),\n }),\n [\n data,\n requestState.called,\n requestState.error,\n requestState.loading,\n customErrors,\n ],\n );\n}\n\nexport function useSnackbarEffect(status, snackbars) {\n const { called, data, error, loading } = status || {};\n const { enqueueSnackbar } = useSnackbar();\n const { error: errorMsg, success: successMsg } = snackbars;\n\n useEffect(() => {\n if (loading || !called) return;\n\n if (error && errorMsg) {\n enqueueSnackbar(errorMsg, {\n autoHideDuration: 5000,\n variant: \"error\",\n });\n }\n\n if (!error && successMsg) {\n enqueueSnackbar(successMsg, {\n variant: \"success\",\n });\n }\n }, [enqueueSnackbar, called, error, loading, data, errorMsg, successMsg]);\n}\n\nexport function useDebounce(value, delay) {\n const [debouncedValue, setDebouncedValue] = useState(value);\n\n useEffect(() => {\n const handler = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n clearTimeout(handler);\n };\n }, [delay, value]);\n\n return debouncedValue;\n}\n\n/**\n * @callback featureFlaggedHookWrapper\n * @param {Function} hook a function, a react hook\n * @returns {Function} the wrapped hook\n */\n/**\n * Avoid calling the hook when the feature is disabled in the shop, it always\n * return the default value if `isEnabled` is false. It is good enhance network\n * performance and avoid graphql error during new feature rollout.\n *\n * @param {boolean} isEnabled\n * @param {*} defaultValue\n * @returns {featureFlaggedHookWrapper}\n */\nexport const featureFlaggedHook =\n (isEnabled, defaultValue = {}) =>\n (hook) =>\n (...args) => {\n if (!isEnabled) {\n return useMemo(() => defaultValue, []);\n }\n return hook(...args);\n };\n\nexport const useDebounceCallback = (func = noop, wait = 500, deps = null) =>\n useCallback(debounce(wait, func), deps || [func]);\n\n/**\n * @function APIFunction\n * @returns {Promise}\n */\n/**\n * @typedef {Object} APIStatus\n * @property {boolean} loading\n * @property {boolean} called\n * @property {Result} [data]\n * @property {Error} [error]\n * @property {Function} reset\n */\n/**\n * Wrapped an async API function to an apollo like function, thus it is similar\n * to `useLazyQuery` and `useMutation`, we got an API function and the status of\n * the API call (e.g. loading, called, data, error, etc.)\n * @param {APIFunction} [apiFunc]\n * @returns {[Function, APIStatus]}\n */\nexport const useApolloCompatibleAPI = (apiFunc = noop) => {\n const [data, setData] = useState();\n const [error, setError] = useState();\n const [loading, setLoading] = useState(false);\n const [called, setCalled] = useState(false);\n const reset = useCallback(() => {\n setData();\n setError();\n setLoading(false);\n setCalled(false);\n }, []);\n\n const status = memoObjectByKeyValues({\n data,\n error,\n loading,\n called,\n reset,\n });\n\n const wrappedFunc = useCallback(\n async (...args) => {\n setLoading(true);\n try {\n const result = await apiFunc(...args);\n setCalled(true);\n setData(result);\n } catch (err) {\n console.error(err);\n setError(err);\n } finally {\n setLoading(false);\n }\n },\n [apiFunc],\n );\n\n return useMemo(() => [wrappedFunc, status], [wrappedFunc, status]);\n};\n","import sessionTracking from \"./sessionTracking\";\n\nexport class Titan {\n constructor({\n endpoint,\n retailerMoniker,\n product = \"returns\",\n ...defaults\n } = {}) {\n this.endpoint = endpoint; // api endpoint, set to empty string for disabling the logging\n this.retailerMoniker = retailerMoniker;\n this.product = product;\n this.defaults = {\n anonymousId: sessionTracking.anonymousId,\n userId: null,\n channel: \"web\",\n context: {},\n ...defaults,\n };\n }\n\n log(type, { props = {}, extra = [], ...payload }) {\n if (this.endpoint === undefined) {\n console.error(new Error(\"Titan endpoint is undefined\"));\n }\n if (!this.retailerMoniker) {\n console.error(new Error(\"Retailer Moniker is undefined\"));\n }\n if (!this.endpoint) return Promise.resolve(false);\n\n const now = new Date().toISOString();\n let data = {\n ...this.defaults,\n retailer_moniker: this.retailerMoniker,\n type,\n event,\n sentAt: now,\n originalTimestamp: now,\n ...payload,\n properties: {\n narvar_product_context: this.product,\n ...props,\n extra_properties: extra,\n },\n };\n\n return fetch(`${this.endpoint}/v1/${type}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${btoa(this.retailerMoniker + \":\")}`,\n },\n body: JSON.stringify(data),\n })\n .then((res) => res.text())\n .then((res) => res === \"OK\")\n .catch((err) => {\n console.warn(err);\n return false;\n });\n }\n\n identify(userId, rest) {\n this.defaults.userId = userId;\n return this.log(\"identify\", { userId, ...rest });\n }\n page(name, rest) {\n return this.log(\"page\", { name, ...rest });\n }\n track(event, rest) {\n return this.log(\"track\", { event, ...rest });\n }\n}\n\nconst titan = new Titan();\nexport default titan;\n\nwindow.titan = titan;\n","import { negate, omit, range, set, flatten } from \"lodash/fp\";\nimport {\n useOrderFetchData,\n useReturnStepsState,\n} from \"../contexts/returnSteps\";\nimport { useCallback } from \"react\";\nimport { RETURN_CREDIT_METHOD_REPLACEMENT } from \"../../retailer-app/constants/returns\";\n\nconst transformReturnItemToExchangeItem = (order, returnItemInput) => {\n const item = order?.itemsById?.[returnItemInput.lineItemId] ?? {};\n return {\n returnItem: returnItemInput,\n newProductId: item.productId,\n newProductVariantId: item.variantId,\n newProductVariantInfo: item.variantInfo.map(omit(\"__typename\")),\n newProductSku: item.sku,\n newProductName: item.productTitle,\n newProductImage: item.productImageUrl,\n newProductPriceAmount: item.priceAmount,\n newProductPriceCurrency: order.presentmentCurrency,\n };\n};\n\nconst useClaims = () => {\n const state = useReturnStepsState();\n const fetchData = useOrderFetchData();\n const order = fetchData.state.data?.order;\n const returnReasonsById = fetchData.state.data?.returnReasonsById;\n\n const selectedClaimReason = state.items.find((item) => item.isClaim);\n const hasClaimItems = !!selectedClaimReason;\n const claimReasonCode = selectedClaimReason?.returnReason;\n const claimType = returnReasonsById?.[claimReasonCode]?.claimType;\n const isClaimReplacement =\n state?.selectedReturnCreditMethod?.id === RETURN_CREDIT_METHOD_REPLACEMENT;\n\n const transformClaimItemsToExchangeItems = useCallback(\n ({ returnItems, exchangeItems, ...rest }) => {\n const isClaim = (item) =>\n !!returnReasonsById?.[item.returnReasonId]?.claimType;\n const isNotClaim = negate(isClaim);\n return {\n ...rest,\n returnItems: returnItems.filter(isNotClaim),\n exchangeItems: [\n ...exchangeItems,\n ...flatten(\n returnItems\n .filter(isClaim)\n .map((item) => transformReturnItemToExchangeItem(order, item))\n .map((item) =>\n // Due to backend limitation, we convert a exchange with quantity > 1\n // into multiple exchanges which quantity is 1\n range(0, item.returnItem.quantity).map(() =>\n set(\"returnItem.quantity\", 1, item),\n ),\n ),\n ),\n ],\n };\n },\n [returnReasonsById],\n );\n const decorateClaimsSubmitPayload = useCallback(\n (payload) =>\n isClaimReplacement\n ? transformClaimItemsToExchangeItems(payload)\n : payload,\n [isClaimReplacement, transformClaimItemsToExchangeItems],\n );\n\n const transformClaimSelectedItemsForNth = useCallback(\n (selectedItems) => {\n return selectedItems.map((item) => {\n return {\n ...item,\n type: isClaimReplacement ? \"exchange\" : item.type,\n exchangeTo: isClaimReplacement\n ? {\n variantInfo: item.variantInfo,\n productId: item.productId,\n variantId: item.variantId,\n name: item.name,\n price: item.price,\n sku: item.sku,\n imageUrl: item.imageUrl,\n available: true,\n availabilityStatus: undefined,\n exchangeForCredit: false,\n }\n : item.exchangeTo,\n };\n });\n },\n [isClaimReplacement],\n );\n\n return {\n hasClaimItems,\n isClaimReplacement,\n claimReasonCode,\n claimType,\n transformClaimItemsToExchangeItems,\n transformClaimSelectedItemsForNth,\n decorateClaimsSubmitPayload,\n };\n};\n\nexport default useClaims;\n","import React from \"react\";\nimport Typography from \"@material-ui/core/Typography\";\nimport KeyboardArrowDown from \"@material-ui/icons/KeyboardArrowDown\";\nimport KeyboardArrowUp from \"@material-ui/icons/KeyboardArrowUp\";\nimport { makeStyles } from \"@material-ui/core/styles\";\nimport PropTypes from \"prop-types\";\nimport clsx from \"clsx\";\n\nconst useStyles = makeStyles(\n () => ({\n root: {\n display: \"flex\",\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n },\n arrows: {\n display: \"flex\",\n },\n arrow: {\n color: \"#cfd0d3\",\n cursor: \"pointer\",\n },\n arrowDisabled: {\n pointerEvents: \"none\",\n },\n }),\n { name: \"N0PositionControl\" },\n);\n\nfunction PositionControl({\n disableDown,\n disableUp,\n id,\n onMoveDown,\n onMoveUp,\n value,\n}) {\n const classes = useStyles();\n\n function handleMoveUp() {\n if (disableUp) return;\n onMoveUp(id);\n }\n\n function handleMoveDown() {\n if (disableDown) return;\n onMoveDown(id);\n }\n\n return (\n
\n {value && {value}}\n
e.stopPropagation()}>\n \n \n
\n
\n );\n}\nPositionControl.propTypes = {\n disableDown: PropTypes.bool,\n disableUp: PropTypes.bool,\n id: PropTypes.any,\n onMoveDown: PropTypes.func,\n onMoveUp: PropTypes.func,\n value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),\n};\nexport default PositionControl;\n","import React from \"react\";\n\nfunction StoreCredit(props) {\n return (\n \n );\n}\n\nexport default StoreCredit;\n","import React, { createContext, useCallback, useContext, useRef } from \"react\";\nimport { getBranding } from \"../../shared/modules/config\";\nimport { scrollToPosition } from \"../../shared/modules/frame\";\nimport { useDebounceCallback } from \"../../shared/modules/hooks\";\n\nexport const FrameDimensionsContext = createContext({\n setActiveSection: (htmlElement) => {},\n scrollToActiveNow: () => {},\n scrollToActive: () => {},\n});\n\nfunction useTypeFormHook() {\n const activeSectionRef = useRef(null);\n\n const setActiveSection = useCallback((htmlElement) => {\n activeSectionRef.current = htmlElement;\n }, []);\n\n const scrollToActiveNow = useCallback(function scrollToActive() {\n // TODO: deprecate soon. Always returning 0.\n // const { top: viewportTop } = getWindowFramePosition();\n const { top } = activeSectionRef.current.getBoundingClientRect();\n const scrollTop = window.pageYOffset || document.documentElement.scrollTop;\n const marginTop = getBranding(\"fixed_app_bar\")\n ? 0\n : getBranding(\"step_delimiter_position\");\n scrollToPosition(top + scrollTop + marginTop);\n }, []);\n\n // Scroll to active section in the next frame (60Hz). If we call this function\n // on a callback (eg. onChange), we usually also set some state in react,\n // which trigger some UI change. So we give some time gap and wait for the\n // re-rendering before we perform the scroll action.\n const scrollToActive = useDebounceCallback(scrollToActiveNow, 16);\n\n return {\n setActiveSection,\n scrollToActiveNow,\n scrollToActive,\n };\n}\n\nexport function useTypeForm() {\n return useContext(FrameDimensionsContext);\n}\n\nexport default function TypeFormProvider({ children }) {\n const state = useTypeFormHook();\n\n return (\n \n {children}\n \n );\n}\n","import { set, merge } from \"lodash/fp\";\nimport React, {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useState,\n useMemo,\n} from \"react\";\nimport ResizeObserver from \"resize-observer-polyfill\";\nimport { getBranding } from \"../../shared/modules/config\";\n\nimport { memoObjectByKeyValues } from \"../../shared/modules/object\";\n\nexport const FrameDimensionsContext = createContext();\n\nconst positiveOnly = (num) => Math.max(num, 0);\n\nfunction useFrameDimensionsHook() {\n const [dimensions, setDimensions] = useState({\n windowHeight: window.parentData?.innerHeight ?? 0,\n offsetTop: window.parentData?.offsetTop ?? 0,\n scrollY: window.parentData?.scrollY ?? 0,\n headerHeight: window.parentData?.headerHeight ?? 0,\n footerHeight: window.parentData?.footerHeight ?? 0,\n frameHeight: window.innerHeight ?? 0,\n });\n\n const initiated = dimensions.windowHeight > 0;\n const isFixed = getBranding(\"fixed_app_bar\");\n\n const viewportHeight = useMemo(() => {\n const bottom = positiveOnly(\n -dimensions.offsetTop + dimensions.windowHeight - dimensions.frameHeight,\n );\n const top = isFixed\n ? dimensions.headerHeight\n : positiveOnly(dimensions.headerHeight - dimensions.scrollY);\n const ret = dimensions.windowHeight - top - bottom;\n return ret;\n }, [dimensions]);\n\n useEffect(() => {\n const eventHandler = (event) => {\n if (event?.data?.type !== \"nvo\" || !event.isTrusted) return;\n\n const { action, data } = event.data;\n const updateMapping = {\n \"receive:innerHeight\": \"windowHeight\",\n \"receive:scrollY\": \"scrollY\",\n \"receive:offsetTop\": \"offsetTop\",\n };\n const updateField = updateMapping[action];\n if (updateField) {\n setDimensions(set(updateField, data || 0));\n } else if (action === \"receive:layoutDimensions\") {\n setDimensions((current) => merge(current, data));\n }\n };\n window.addEventListener(\"message\", eventHandler);\n\n return () => window.removeEventListener(\"message\", eventHandler);\n }, []);\n\n useEffect(() => {\n const observer = new ResizeObserver((entries) => {\n setDimensions(set(\"frameHeight\", entries[0].contentRect.height));\n });\n observer.observe(document.querySelector(\"body\"));\n\n return () => observer.disconnect();\n }, []);\n\n const bottomOfViewport = useCallback(\n (bottom = 0) =>\n initiated\n ? positiveOnly(\n dimensions.frameHeight -\n dimensions.windowHeight +\n dimensions.offsetTop +\n bottom,\n )\n : 0,\n [initiated, dimensions],\n );\n\n const topOfViewport = useCallback(\n (top = 0) =>\n initiated\n ? positiveOnly(\n -dimensions.offsetTop +\n top +\n (isFixed ? dimensions.headerHeight : 0),\n )\n : 0,\n [initiated, dimensions],\n );\n\n return memoObjectByKeyValues({\n initiated,\n dimensions: memoObjectByKeyValues({\n ...dimensions,\n viewportHeight,\n }),\n bottomOfViewport,\n topOfViewport,\n });\n}\n\nexport function useFrameDimensions() {\n return useContext(FrameDimensionsContext);\n}\n\nexport default function FrameDimensionsProvider({ children }) {\n const state = useFrameDimensionsHook();\n\n return (\n \n {children}\n \n );\n}\n","import { mapKeys, snakeCase } from \"lodash/fp\";\n\nexport class Noflake {\n constructor({ endpoint, defaults } = {}) {\n this.endpoint = endpoint; // api endpoint, set to empty string for disabling the logging\n this.defaults = {\n ...defaults,\n };\n }\n\n log(schema, payload) {\n if (this.endpoint === undefined) {\n console.error(new Error(\"Noflake endpoint is undefined\"));\n }\n if (!this.endpoint) return Promise.resolve(false);\n\n const now = new Date().toISOString();\n let data = {\n ...this.defaults,\n tag: schema ? `noflake.${schema}` : this.defaults.tag,\n event_ts: now,\n ingestion_timestamp: now,\n ...payload,\n };\n data = mapKeys(snakeCase)(data);\n\n return fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(data),\n })\n .then((res) => {\n return res.ok;\n })\n .catch((err) => {\n console.warn(err);\n return false;\n });\n }\n}\n\nconst noflake = new Noflake();\nexport default noflake;\n","import escapeStringRegexp from \"escape-string-regexp\";\n\nexport const IN_STOCK = \"in-stock\";\nexport const UNAVAILABLE_IN_THIS_SET = \"not available in this set\";\nexport const UNAVAILABLE_IN_ANY_SET = \"not available in any set\";\n\nexport type SparsemOption = string | undefined;\nexport type SparsemStatus =\n | typeof IN_STOCK\n | typeof UNAVAILABLE_IN_THIS_SET\n | typeof UNAVAILABLE_IN_ANY_SET;\nexport type SparsemSearchToken = SparsemOption | null | undefined;\nexport type SparsemSearchResult = {\n [option: string]: SparsemStatus;\n};\n\nfunction maxBy(list: T[], getScore: (object: T) => number): T | undefined {\n let maxScore = 0;\n let max: T | undefined = list[0];\n list.forEach((item) => {\n const score = getScore(item);\n if (score > maxScore) {\n maxScore = score;\n max = item;\n }\n });\n return max;\n}\n\n/**\n * Sparsem class cloned from `narvar/shopify-zero-retailer`. It allows you to\n * search availability status of a specify exchange option.\n */\nexport default class Sparsem {\n SEP = \"\\t\";\n BLANK = \"__BLANK__\";\n arr: string[] = [];\n inp: SparsemOption[][] = [];\n opts: SparsemOption[][] = [];\n\n get num(): number {\n return this.opts.length;\n }\n\n setArr(availableItems: SparsemOption[][], optionsConfig: SparsemOption[][]) {\n this.inp = availableItems;\n this.opts = optionsConfig;\n this.arr = availableItems.map((x) =>\n x.map((y) => y ?? this.BLANK).join(this.SEP),\n );\n }\n\n search(options: SparsemSearchToken[] = []): SparsemSearchResult {\n const str = options\n .map((option, i) => {\n if (i >= this.num) return;\n if (option) return escapeStringRegexp(option);\n return \".{1,}\";\n })\n .filter(Boolean)\n .join(this.SEP);\n\n const out = this.arr\n .filter((x) => x.match(new RegExp(`^${str}$`)))\n .map((x) => x.split(this.SEP));\n\n const missingIdx = options.indexOf(null);\n\n if (!this.opts[missingIdx]) return {};\n\n const result: SparsemSearchResult = this.opts[missingIdx].reduce(\n (ret, val) => {\n ret[val ?? \"\"] = out.some((x) => x[missingIdx] === val)\n ? IN_STOCK\n : this.inp.some((x) => x[missingIdx] === val)\n ? UNAVAILABLE_IN_THIS_SET\n : UNAVAILABLE_IN_ANY_SET;\n return ret;\n },\n {} as SparsemSearchResult,\n );\n\n return result;\n }\n\n select(options: SparsemSearchToken[]): SparsemOption[] | undefined {\n const result = maxBy(this.inp, (availableItem) =>\n availableItem.reduce((score, itemOption, index) => {\n const match = options[index] === itemOption;\n return match ? score + 1 : score;\n }, 0),\n );\n return result;\n }\n}\n","import { template, unescape } from \"lodash/fp\";\nimport templateSettings from \"lodash/templateSettings\";\nimport errorNotifier from \"./error-notifier\";\n\nexport const TEMPLATE_VAR_REGEX = /%{([\\s\\S]+?)}/g;\n\ntemplateSettings.escape = TEMPLATE_VAR_REGEX;\ntemplateSettings.evaluate = TEMPLATE_VAR_REGEX;\ntemplateSettings.interpolate = TEMPLATE_VAR_REGEX;\n\nexport const interpolate = (string, variables) => {\n try {\n return unescape(template(string)(variables));\n } catch (err) {\n console.warn(err);\n console.warn(`Template interpolate error: \"${string}\"`, variables);\n err.message = \"Template interpolate error\";\n errorNotifier.warn(err, { template: string, variables });\n return unescape(string);\n }\n};\n","import React from \"react\";\nimport PropTypes from \"prop-types\";\n\nimport Button from \"@material-ui/core/Button\";\nimport Grid from \"@material-ui/core/Grid\";\nimport SnackbarContent from \"@material-ui/core/SnackbarContent\";\nimport TextField from \"@material-ui/core/TextField\";\nimport Typography from \"@material-ui/core/Typography\";\n\nimport { makeRootStyles } from \"../../theme/styles\";\n\nimport { config } from \"../../config\";\nconst { isGiftZipEnabled, isGiftReceiptEnabled } = config;\n\nconst useStyles = makeRootStyles(\n (theme) => ({\n root: {},\n form: {\n marginBottom: theme.spacing(1),\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n instructions: {\n marginBottom: theme.spacing(1),\n lineHeight: 1.25,\n },\n formFieldContainer: {\n width: \"100%\",\n },\n input: {\n backgroundColor: theme.palette.common.white,\n },\n submitButton: {\n height: theme.spacing(6),\n },\n errorSnackbar: {\n color: theme.palette.error.text,\n backgroundColor: theme.palette.error.main,\n fontSize: \"100%\",\n },\n giftReturnTitle: {\n fontWeight: \"bold\",\n borderTop: \"1px solid #c5c5c5\",\n marginTop: theme.spacing(2),\n paddingTop: theme.spacing(2),\n },\n giftReturnLink: {\n marginTop: theme.spacing(1),\n textAlign: \"center\",\n color: theme.palette.primary.main,\n },\n giftEmailHelperText: {\n color: \"#767676\",\n fontSize: \"0.75rem\",\n },\n }),\n { name: \"N0OrderLookupLogin\" },\n);\n\nfunction OrderLookupLogin({\n orderNumber,\n email,\n giftField,\n error,\n onChangeOrderNumber,\n onChangeEmail,\n onChangeGiftField,\n onSubmitForm,\n disabled,\n isTrack,\n isGift,\n onChangeIsGift,\n}) {\n const classes = useStyles();\n const buttonDisabled = !(orderNumber && email) || disabled;\n\n const switchReturnType = () => {\n onChangeIsGift(!isGift);\n };\n\n const giftReturnTitle = isTrack\n ? isGift\n ? config.translations.track_app_order_lookup_not_gift_link\n : config.translations.track_app_order_lookup_gift_link\n : isGift\n ? config.translations.order_lookup_not_gift_link\n : config.translations.order_lookup_gift_link;\n const giftReturnLink = isTrack\n ? isGift\n ? config.translations.track_app_order_lookup_title\n : config.translations.track_app_order_lookup_gift_title\n : isGift\n ? config.translations.order_lookup_title_start_return\n : config.translations.order_lookup_gift_title_start_return;\n const instructions = isTrack\n ? isGift\n ? config.translations.track_app_order_lookup_gift_start_instructions\n : config.translations.track_app_order_lookup_start_return_instructions\n : isGift\n ? config.translations.order_lookup_gift_start_instructions\n : config.translations.order_lookup_start_return_instructions;\n const input3 = isGiftZipEnabled\n ? config.translations.order_lookup_gift_shipping_zip\n : config.translations.order_lookup_gift_code;\n const orderNumberTranslation = isGift\n ? config.translations.order_lookup_gift_order_number\n : config.translations.order_lookup_order_number;\n const emailTranslation = isGift\n ? config.translations.order_lookup_gift_email\n : config.translations.order_lookup_email;\n const notFoundErrorMessage = isTrack\n ? isGift\n ? config.translations.track_app_order_lookup_not_found_gift\n : config.translations.track_app_order_lookup_not_found\n : isGift\n ? config.translations.order_lookup_not_found_gift\n : config.translations.order_lookup_not_found;\n const giftEmailHelper = isTrack\n ? config.translations.track_app_order_lookup_gift_email_helper\n : config.translations.order_lookup_gift_email_helper;\n const giftCodeHelper = isTrack\n ? config.translations.track_app_order_lookup_gift_code_helper\n : config.translations.order_lookup_gift_code_helper;\n const submitLabel = isTrack\n ? config.translations.track_app_order_lookup_submit\n : config.translations.order_lookup_submit;\n\n return (\n \n );\n}\nOrderLookupLogin.propTypes = {\n email: PropTypes.string,\n orderNumber: PropTypes.string,\n giftField: PropTypes.string,\n disabled: PropTypes.bool,\n isGift: PropTypes.bool,\n isTrack: PropTypes.bool,\n error: PropTypes.bool,\n onChangeOrderNumber: PropTypes.func,\n onChangeEmail: PropTypes.func,\n onChangeGiftField: PropTypes.func,\n onSubmitForm: PropTypes.func,\n onChangeIsGift: PropTypes.func,\n};\nexport default React.memo(OrderLookupLogin);\n","import React, { useCallback, useState } from \"react\";\nimport PropTypes from \"prop-types\";\nimport Grid from \"@material-ui/core/Grid\";\nimport Typography from \"@material-ui/core/Typography\";\nimport Link from \"@material-ui/core/Link\";\nimport Paper from \"@material-ui/core/Paper\";\n\nimport { makeRootStyles } from \"../../theme/styles\";\nimport ConfirmationDialog from \"../../../shared/components/modal/ConfirmationDialog\";\nimport Spinner from \"../../../shared/components/Spinner\";\nimport { config } from \"../../config\";\nimport { interpolate } from \"../../../shared/modules/template\";\n\nexport const useStyles = makeRootStyles(\n (theme) => ({\n root: {\n paddingTop: theme.spacing(2),\n paddingBottom: theme.spacing(2),\n justifyContent: \"center\",\n },\n title: {\n marginBottom: theme.spacing(2),\n wordWrap: \"break-word\",\n },\n container: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexDirection: \"column\",\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n padding: theme.spacing(2),\n width: \"100%\",\n },\n cancel: {\n textTransform: \"capitalize\",\n },\n }),\n { name: \"N0ShopNowExpiration\" },\n);\n\nconst ShopNowExpiration = ({ value, code, onCancel, cancelStatus = {} }) => {\n const classes = useStyles();\n const [open, setOpen] = useState(false);\n const showCancelReturn = () => setOpen(true);\n const cancelCancelReturn = () => setOpen(false);\n\n const confirmCancelReturn = useCallback(\n function confirmCancelReturn() {\n setOpen(false);\n onCancel(code);\n },\n [onCancel],\n );\n\n if (!value) return null;\n\n return (\n \n \n \n <>\n {!cancelStatus.data && !cancelStatus.loading && (\n <>\n \n {interpolate(\n config.translations.shopnow_outstanding_credit_alert,\n { amount: value },\n )}\n \n \n {config.translations.cancel}\n \n \n >\n )}\n {cancelStatus.loading && !cancelStatus.error && }\n {cancelStatus.data && (\n \n {config.translations.shopnow_cancel_session_success}\n \n )}\n >\n \n \n \n );\n};\nShopNowExpiration.propTypes = {\n value: PropTypes.string,\n code: PropTypes.string,\n onCancel: PropTypes.func,\n cancelStatus: PropTypes.any,\n};\nexport default ShopNowExpiration;\n","import React, {\n useCallback,\n useMemo,\n useState,\n useEffect,\n useRef,\n} from \"react\";\nimport Grid from \"@material-ui/core/Grid\";\nimport Typography from \"@material-ui/core/Typography\";\nimport PropTypes from \"prop-types\";\nimport { useGrowthBook } from \"@growthbook/growthbook-react\";\nimport get from \"lodash/get\";\n\nimport { OrderLookup as NthOrderLookup } from \"@narvar/nth-kit-returns-headless\";\n\nimport Spinner from \"../../../shared/components/Spinner\";\nimport { config } from \"../../config\";\nimport {\n useReturnStepsActions,\n useReturnStepsState,\n CHOOSE_ITEMS_STEP,\n ORDER_LOOKUP_STEP,\n} from \"../../contexts/returnSteps\";\nimport { useEffectOnce } from \"../../../shared/modules/hooks\";\nimport { makeRootStyles } from \"../../theme/styles\";\nimport OrderLookupDetails from \"./OrderLookupDetails\";\nimport OrderLookupLogin from \"./OrderLookupLogin\";\nimport ShopNowExpiration from \"./ShopNowExpiration\";\nimport Spacer from \"../../../shared/components/Spacer\";\nimport { updateGrowthBookAttributes } from \"../../../shared/modules/growthBook\";\nimport {\n countingEvent,\n LOGIN_SUCCESS_EVENT,\n useAbTesting,\n} from \"../../data/abTesting\";\nimport useLoginSession from \"../../hooks/useLoginSession\";\nimport { toggleContainerClass } from \"../../../shared/modules/frame\";\nimport { getBranding } from \"../../../shared/modules/config\";\nimport titan from \"../../../shared/modules/titan\";\n\nexport const useStyles = makeRootStyles(\n (theme) => ({\n root: {\n paddingTop: theme.spacing(4),\n paddingBottom: theme.spacing(8),\n justifyContent: \"center\",\n },\n title: {\n marginBottom: theme.spacing(2),\n wordWrap: \"break-word\",\n },\n supplementaryText1: {\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n },\n supplementaryText2: {\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n },\n }),\n { name: \"N0OrderLookup\" },\n);\n\nconst OrderLookup = ({\n onOrderFetch,\n orderFetchData,\n orderFetchError,\n orderFetchLoading,\n\n shopNowData,\n checkShopNow,\n cancelShopNow,\n shopNowCancelData,\n shopNowCancelError,\n shopNowCancelLoading,\n}) => {\n const classes = useStyles();\n const state = useReturnStepsState();\n const actions = useReturnStepsActions();\n const growthbook = useGrowthBook();\n const abTesting = useAbTesting();\n const loginSession = useLoginSession();\n\n const orderFetchDataRef = useRef(orderFetchData);\n\n const [orderNumber, setOrderNumber] = useState(state.orderNumber);\n const [email, setEmail] = useState(state.email);\n const [giftField, setGiftField] = useState(state.giftField);\n const isGift = state.guestReturn;\n const isGiftReturnsEnabled =\n config.isGiftZipEnabled || config.isGiftReceiptEnabled;\n const [shopNowCode, setShopNowCode] = useState();\n const csid = state.csid;\n const useNewDesign = config.isNthLoginUi;\n const newDesignLayout = getBranding(\"background_image_url\")\n ? \"default\"\n : \"centered\";\n const isTrack = state.mode === \"track\";\n\n const newUIValues = useMemo(\n () =>\n [\n { type: \"ORDER\", value: orderNumber },\n { type: \"EMAIL\", value: email },\n isGift ? { type: \"GIFT_CODE\", value: giftField } : null,\n ].filter((d) => d),\n [isGift, orderNumber, email, giftField],\n );\n\n const details = useMemo(\n () => (!orderFetchError && orderFetchData && orderFetchData.order) || null,\n [orderFetchData, orderFetchError],\n );\n\n const returnHistory = useMemo(\n () =>\n (!orderFetchError && orderFetchData && orderFetchData.returnHistory) ||\n null,\n [orderFetchData, orderFetchError],\n );\n\n const ineligible = !!details?.eligible;\n\n // reset local state when the global state changes\n useEffect(() => {\n setOrderNumber(state.orderNumber);\n setEmail(state.email);\n setGiftField(state.giftField);\n }, [state.orderNumber, state.email, state.giftField]);\n\n const handleExit = () => {\n orderFetchDataRef.current = null;\n\n setOrderNumber(\"\");\n setEmail(\"\");\n\n loginSession.logout();\n if (useNewDesign) {\n toggleContainerClass(\"nvo_container--new-login\");\n toggleContainerClass(\"nvo_container--logged-in\");\n }\n };\n\n const handleChangeOrderNumber = (event) => setOrderNumber(event.target.value);\n const handleChangeEmail = (event) => setEmail(event.target.value);\n const handleChangeGiftField = (event) => setGiftField(event.target.value);\n\n const handleChangeIsGift = (val) => {\n actions.setIsGuestReturn(val);\n };\n\n const trackingSetPerson = (orderNumber, isGift) => {\n const shopDomain = config.shopDomain;\n const uid = `${shopDomain}::${orderNumber}`;\n\n updateGrowthBookAttributes(growthbook, {\n userId: uid,\n email,\n loggedIn: true,\n });\n\n titan.identify(uid);\n };\n\n const handleSubmitForm = useCallback(\n function handleSubmitForm(event) {\n event?.preventDefault?.();\n actions.setEmail(email);\n actions.setOrderNumber(orderNumber);\n\n orderFetchDataRef.current = null;\n\n onOrderFetch({\n email,\n orderNumber,\n giftField,\n isGift,\n csid,\n });\n },\n [actions, onOrderFetch, email, orderNumber, isGift, giftField, csid],\n );\n\n const loginNode = useMemo(\n () => (\n <>\n \n >\n ),\n [\n email,\n handleSubmitForm,\n orderFetchError,\n orderFetchLoading,\n orderNumber,\n isGift,\n isTrack,\n ],\n );\n\n const { isShopNowEnabled } = config;\n if (isShopNowEnabled) {\n useEffect(() => {\n if (shopNowCancelError || shopNowCancelLoading || !shopNowCancelData) {\n return;\n }\n\n window.localStorage.removeItem(\"nv_shop_now_checkout\");\n }, [\n shopNowCancelData,\n shopNowCancelError,\n shopNowCancelLoading,\n cancelShopNow,\n ]);\n }\n\n const item = isShopNowEnabled\n ? window.localStorage.getItem(\"nv_shop_now_checkout\")\n : null;\n\n const shopNowValue = get(\n shopNowData,\n \"checkShopNowExpiration.formattedAmount\",\n );\n const isShopNowExpired = get(shopNowData, \"checkShopNowExpiration.expired\");\n const handleCancelShopNow = (code) => cancelShopNow(code);\n\n useEffect(() => {\n if (!orderFetchDataRef.current && orderFetchData) {\n orderFetchDataRef.current = orderFetchData;\n\n actions.setGuestEmail(\n get(orderFetchData, \"order.guestLoginEmail\", email),\n );\n actions.goToStep(CHOOSE_ITEMS_STEP);\n\n trackingSetPerson(orderNumber, isGift);\n abTesting.trackResult(countingEvent(LOGIN_SUCCESS_EVENT));\n if (useNewDesign) {\n toggleContainerClass(\"nvo_container--new-login\");\n toggleContainerClass(\"nvo_container--logged-in\");\n }\n }\n }, [actions, ineligible, orderFetchData, orderFetchError]);\n\n useEffectOnce(() => {\n if (isGift && orderNumber && email && giftField) {\n onOrderFetch({ orderNumber, email, isGift, giftField, csid });\n } else if (orderNumber && email) {\n onOrderFetch({ orderNumber, email, isGift, csid });\n }\n\n // TODO: error handling\n if (item) {\n const searchString = new URL(item).search;\n const params = new URLSearchParams(searchString);\n const code = params.get(\"nrvr_code\");\n setShopNowCode(code);\n checkShopNow(code);\n }\n });\n\n const translation1 = isTrack\n ? config.translations.track_app_order_lookup_supplementary_text1\n : config.translations.order_lookup_supplementary_text1;\n const translation2 = isTrack\n ? config.translations.track_app_order_lookup_supplementary_text2\n : config.translations.order_lookup_supplementary_text2;\n const title = isTrack\n ? isGift\n ? config.translations.track_app_order_lookup_gift_title\n : config.translations.track_app_order_lookup_title\n : isGift\n ? config.translations.order_lookup_gift_title_start_return\n : config.translations.order_lookup_title_start_return;\n\n const shopNowSessionReminder = !isShopNowExpired && (\n \n );\n\n // after login, if new UI was enabled, we'll also skip the order details header\n if (state.stepStatus[ORDER_LOOKUP_STEP]?.skipped && details) {\n if (shopNowCode && shopNowSessionReminder) {\n return (\n \n \n {shopNowSessionReminder}\n \n \n );\n } else {\n return null;\n }\n }\n\n if (useNewDesign && !details) {\n return (\n \n \n {\n const values = e.value ?? [];\n const stateSetter = (type, setter) =>\n setter(values.find((v) => v.type === type)?.value);\n stateSetter(\"ORDER\", setOrderNumber);\n stateSetter(\"EMAIL\", setEmail);\n stateSetter(\"GIFT_CODE\", setGiftField);\n }}\n onClose={(e) => {\n const toggleGiftReturn = e.subComponent === \"return\";\n actions.setIsGuestReturn(toggleGiftReturn);\n }}\n onSubmit={(e) => {\n handleSubmitForm();\n }}\n />\n \n \n );\n }\n\n return (\n \n \n {shopNowSessionReminder}\n {details ? (\n \n ) : (\n <>\n {translation1 && translation1 !== \"\" && (\n \n \n \n \n \n )}\n \n {title}\n \n\n {loginNode}\n\n {translation2 && translation2 !== \"\" && (\n \n \n \n \n \n )}\n {orderFetchLoading && (\n <>\n \n \n >\n )}\n >\n )}\n \n \n );\n};\nOrderLookup.propTypes = {\n onOrderFetch: PropTypes.func,\n orderFetchData: PropTypes.object,\n orderFetchError: PropTypes.any,\n orderFetchLoading: PropTypes.any,\n\n shopNowData: PropTypes.any,\n checkShopNow: PropTypes.func,\n cancelShopNow: PropTypes.func,\n shopNowCancelData: PropTypes.any, // apollo data\n shopNowCancelError: PropTypes.any, // apollo error\n shopNowCancelLoading: PropTypes.bool, // apollo loading flag\n};\nexport default React.memo(OrderLookup);\n","import { makeRootStyles } from \"../../../theme/styles\";\n\nconst useStyles = makeRootStyles(\n {\n selectItem: {\n boxSizing: \"border-box\",\n border: \"0 none\",\n padding: \"16px\",\n cursor: \"pointer\",\n borderRadius: \"4px\",\n backgroundColor: \"inherit\",\n fontFamily: \"inherit\",\n fontWeight: \"inherit\",\n color: \"inherit\",\n textAlign: \"left\",\n },\n hoverBorder: {\n \"&:hover\": {\n borderWidth: \"1px\",\n borderStyle: \"solid\",\n },\n },\n noBorder: {\n borderTopColor: \"none\",\n borderLeftColor: \"none\",\n borderRightColor: \"none\",\n borderBottomColor: \"none\",\n borderTopWidth: \"0\",\n borderLeftWidth: \"0\",\n borderRightWidth: \"0\",\n borderBottomWidth: \"0\",\n borderStyle: \"none\",\n },\n },\n { name: \"N0SelectItem\" },\n);\n\nexport { useStyles };\n","import React, { CSSProperties, HTMLProps, useState } from \"react\";\nimport clsx from \"clsx\";\nimport { useTheme, withThemeProvider } from \"@narvar/nth-hook-theme\";\nimport { useStyles } from \"./SelectItemStyles\";\n\nexport type StyledSelectItemProps = {\n selected?: boolean;\n readOnly?: boolean;\n borderless?: boolean;\n transparent?: boolean;\n customStyle?: CSSProperties;\n};\n\nexport type SelectItemProps = StyledSelectItemProps &\n Omit, \"type\" | \"ref\"> & {\n type?: \"button\" | \"submit\" | \"reset\";\n as?: \"button\" | \"a\" | \"div\" | \"span\";\n };\n\nconst SelectItem = ({\n onClick,\n selected,\n readOnly,\n disabled,\n borderless,\n transparent,\n customStyle,\n children,\n}: SelectItemProps) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const styles: any = useStyles();\n const {\n documentbodyNoneNone,\n bodytextDefaultNone,\n panelcontainerNoneNone,\n selecteditemNoneNone,\n buttonsSecondaryDisabled,\n } = useTheme();\n\n const [hover, setHover] = useState(false);\n\n const classes = clsx({\n [styles.selectItem]: true,\n [styles.hoverBorder]: !selected && !readOnly && !disabled && !borderless,\n [styles.noBorder]: borderless,\n });\n\n const defaultBackgroundColor =\n documentbodyNoneNone?.backgroundColor ??\n panelcontainerNoneNone?.backgroundColor;\n\n const borderStyle = {\n borderTopColor: selecteditemNoneNone?.borderTopColor ?? \"#000\",\n borderLeftColor: selecteditemNoneNone?.borderLeftColor ?? \"#000\",\n borderRightColor: selecteditemNoneNone?.borderRightColor ?? \"#000\",\n borderBottomColor: selecteditemNoneNone?.borderBottomColor ?? \"#000\",\n };\n\n return (\n \n );\n};\n\nexport default withThemeProvider(SelectItem, {\n themeTokens: [\n \"documentbodyNoneNone\",\n \"bodytextDefaultNone\",\n \"panelcontainerNoneNone\",\n \"selecteditemNoneNone\",\n \"buttonsSecondaryDisabled\",\n ],\n});\n","import { omitNullValues } from \"./object\";\n\nexport const buildToolbarHeightForProp = (\n muiTheme,\n cssProp = \"minHeight\",\n negative,\n) => ({\n [cssProp]: negative ? -64 : 64,\n [`${muiTheme.breakpoints.up(\"xs\")} and (orientation: landscape)`]: {\n [cssProp]: negative ? -48 : 48,\n },\n [muiTheme.breakpoints.up(\"sm\")]: {\n [cssProp]: negative ? -80 : 80,\n },\n});\n\n/**\n * Helper for adjust grid responsive config, it is useful when you are building\n * a shared component and expect there will be nested girds in parent.\n * @param {object} config grid sizing config eg. {xs: 12, sm: 10, md: 8, lg: 6, xl: 4}\n * @param {string|number} size small / medium / large / -2, -1, 0, 1, ...\n * @returns {object}\n */\nexport const controlGridSize = (config, size) => {\n const mapping = { medium: 0, small: -1, large: 1 };\n let dir = mapping[size] !== undefined ? mapping[size] : size;\n if (dir === 0 || isNaN(dir)) return config;\n\n let cfg = [config.xs, config.sm, config.md, config.lg, config.xl];\n const modify = dir > 0 ? \"shift\" : \"unshift\";\n dir = Math.abs(dir);\n for (let i = 0; i < dir; i++) {\n cfg[modify](undefined);\n }\n\n return omitNullValues({\n xs: cfg[0],\n sm: cfg[1],\n md: cfg[2],\n lg: cfg[3],\n xl: cfg[4],\n });\n};\n","import { isString } from \"lodash\";\n\n/**\n * @param {string[]|number[]} array\n * @param {string} [connectiveText=and]\n * @returns {string}\n * @example\n * joinHumanize(['1', '2', '3'], 'and') // returns '1, 2 and 3'\n */\nexport const joinHumanize = (array, connectiveText = \"and\") => {\n let ret = [].concat(array); // clone array\n const last = ret.pop();\n ret = ret.join(\", \");\n ret = [].concat(ret || []).concat(last || []);\n ret = ret.join(` ${connectiveText} `);\n return ret;\n};\n\n/**\n * @param {string[]|number[]} array\n * @returns {string}\n * @example\n * joinAnd(['1', '2', '3']) // returns '1, 2 and 3'\n */\nexport const joinAnd = (array) => joinHumanize(array, \"and\");\n\n/**\n * @param {string[]|number[]} array\n * @returns {string}\n * @example\n * joinOr(['1', '2', '3']) // returns '1, 2 or 3'\n */\nexport const joinOr = (array) => joinHumanize(array, \"or\");\n\nconst SIZE_MULTIPLIER_MAP = {\n K: 1 << 10,\n KB: 1 << 10,\n M: 1 << 20,\n MB: 1 << 20,\n G: 1 << 30,\n GB: 1 << 30,\n T: 1 << 40,\n TB: 1 << 40,\n};\n\n/**\n * Parse size string to the number of bytes\n * @param {string} size size in text format, eg. 10KB\n * @returns {number}\n * @example\n * parseSize('1.5kb') // returns 1536\n */\nexport const parseSize = (size) => {\n const matches = size.match(/([0-9.]+)\\s*([a-z]*)/i) || [];\n const num = parseFloat(matches[1] || 0);\n const unit = (matches[2] || \"\").toUpperCase();\n const multiplier = SIZE_MULTIPLIER_MAP[unit] || 1;\n return num === 0 ? 0 : num * multiplier;\n};\n\n/**\n * Format the size in number of bytes to a readable string\n * @param {number} size file size\n * @param {number} [decimals=1] rounding to decimal points\n * @returns {string}\n * @example\n * formatSize(3000, 2) // returns '2.93 KB'\n */\nexport const formatSize = (size, decimals = 1) => {\n if (size === 0) return \"0 Bytes\";\n\n const k = 1024;\n const dm = decimals < 0 ? 0 : decimals;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"];\n\n const i = Math.floor(Math.log(size) / Math.log(k));\n return parseFloat((size / Math.pow(k, i)).toFixed(dm)) + \" \" + sizes[i];\n};\n\n/**\n * Try to parse JSON string, it fallback to default value if there is any errors\n * @param {string} jsonString a JSON string to parse\n * @param {*} [defaultValue=undefined] default value if parse failed\n * @returns {*}\n */\nexport const parseJSON = (jsonString, defaultValue = undefined) => {\n try {\n return JSON.parse(jsonString);\n } catch (err) {\n return defaultValue;\n }\n};\n\nexport const isValidHttpUrl = (string) => {\n let url;\n try {\n url = new URL(string);\n } catch (_) {\n return false;\n }\n return url.protocol === \"http:\" || url.protocol === \"https:\";\n};\n\nexport const isPresentString = (obj) => {\n return isString(obj) && obj.trim() !== \"\";\n};\n","import { useReturnsConfig } from \"@narvar/nth-kit-returns-headless\";\n\nconst useCurrencyConfig = () => {\n const { localeCountry, localeLanguage, includeCurrency } = useReturnsConfig();\n\n return {\n locale: `${localeLanguage}_${localeCountry}`,\n includeCurrency: !!includeCurrency,\n };\n};\n\nexport default useCurrencyConfig;\n","import { config } from \"../config\";\nimport { camelCase, compose, fromPairs, map, set } from \"lodash/fp\";\nimport * as sharedConfig from \"../../shared/modules/config\";\nimport { mergeIgnoreNil } from \"../../shared/modules/object\";\n\n/**\n * @typedef {Object} Address\n * @property {string} firstName\n * @property {string} lastName\n * @property {string} address1\n * @property {string} address2\n * @property {string} city\n * @property {string} zip\n * @property {string} province\n * @property {string} provinceCode\n * @property {string} country\n * @property {string} countryCode\n * @property {string} countryCodeV2\n */\nexport const ADDRESS_DEFAULT = {\n firstName: \"\",\n lastName: \"\",\n address1: \"\",\n address2: \"\",\n city: \"\",\n zip: \"\",\n province: \"\",\n provinceCode: \"\",\n country: \"\",\n countryCode: \"\",\n countryCodeV2: \"\",\n};\n\nexport const getCountryNameByCode = (countryCode) => {\n return (\n config.countryNamesByCode[countryCode] ||\n sharedConfig.countryCodes.find(\n (country) => country.country_code === countryCode,\n )?.country ||\n \"\"\n );\n};\n\n/**\n * Merge address object, the later arguments will overwrite the existing attributes\n * @param {Address} address\n * @param {...Address} updates\n * @returns {Address}\n */\nexport const mergeAddress = (address, ...updates) => {\n const addr = mergeIgnoreNil(ADDRESS_DEFAULT, address, ...updates);\n const countryCode = addr?.countryCode || addr?.countryCodeV2 || \"\";\n addr.countryCode = addr.countryCodeV2 = countryCode;\n addr.country = getCountryNameByCode(countryCode);\n return addr;\n};\n\n/**\n * Patch zip code for address object. For example Hong Kong address may not have\n * a zip code.\n * @param {Address} address\n * @returns {Address}\n */\nexport const patchZipCode = (address) => {\n let zip = address?.zip;\n if (address?.countryCode === \"HK\" || address?.countryCodeV2 === \"HK\") {\n // Hong Kong Post suggested to fill \"000\", \"0000\", \"000000\" or \"HKG\"\n // https://www.hongkongpost.hk/en/about_us/tips/postcode/index.html\n zip = \"000000\";\n }\n\n if (zip !== address?.zip) {\n return { ...address, zip };\n }\n return address;\n};\n\n/**\n * Pick and rename address object to a standard address object\n * @param {any} address\n * @param {String} [attrPrefix]\n * @param {String} [attrSuffix]\n * @returns {Address}\n * @example\n * const addr1 = normalizeAddressKeys({billingZip: '', billingCountry: 'HK'}, 'billing');\n * // addr1 = {zip: '', country: 'HK', firstName: '', ... }\n *\n * const addr2 = normalizeAddressKeys({zip2: '', country2: 'HK'}, '', '2');\n * // addr2 = {zip: '', country: 'HK', firstName: '', ... }\n */\nexport const normalizeAddressKeys = (\n address,\n attrPrefix = \"\",\n attrSuffix = \"\",\n) =>\n compose(\n fromPairs,\n map((key) => {\n const oldKey = camelCase([attrPrefix, key, attrSuffix].join(\"_\"));\n const value = address?.[oldKey] ?? null;\n return [key, value];\n }),\n )(Object.keys(ADDRESS_DEFAULT));\n","import { useMemo, useCallback } from \"react\";\nimport { useMutation, gql } from \"@apollo/client\";\nimport { memoObjectByKeyValues } from \"../../shared/modules/object\";\n\nconst CANCEL_RETURN = gql`\n mutation cancelReturn(\n $orderNumber: String!\n $email: String!\n $returnId: String\n ) {\n cancelReturn(orderNumber: $orderNumber, email: $email, returnId: $returnId)\n }\n`;\n\nexport function useCancelReturn() {\n const [mutate, { loading, error, data }] = useMutation(CANCEL_RETURN);\n const cancelReturn = useCallback(\n (variables) => mutate({ variables }),\n [mutate],\n );\n\n const cancelStatus = useMemo(\n () => ({\n loading,\n error,\n data: data?.cancelReturn ?? null,\n }),\n [loading, error, data],\n );\n\n return useMemo(\n () => ({\n cancelReturn,\n cancelStatus,\n }),\n [cancelReturn, cancelStatus],\n );\n}\n","import { SVGProps } from \"react\";\nimport Truck from \"./truck\";\nimport Locate from \"./locate\";\nimport Store from \"./store\";\nimport Phone from \"./phone\";\nimport Package from \"./PackageIcon\";\nimport { IconMap } from \"./types\";\nimport Clock from \"./clock\";\nimport CheckMark from \"./CheckMark\";\nimport PlaceholderThumbnail from \"./PlaceholderThumbnail\";\nimport ArrowRight from \"./ArrowRight\";\nimport Exchange from \"./Exchange\";\nimport PlaceholderPictures from \"./PlaceholderPictures\";\nimport OriginalPayment from \"./OriginalPayment\";\nimport GiftCard from \"./GiftCard\";\nimport StoreCredit from \"./StoreCredit\";\nimport ShippingLabel from \"./ShippingLabel\";\nimport Printerless from \"./Printerless\";\nimport NarvarBadge from \"./NarvarBadge\";\nimport Favorite from \"./Favorite\";\nimport CheckCircle from \"./CheckCircle\";\nimport Error from \"./Error\";\nimport Reshop from \"./Reshop\";\nimport ShippingProtection from \"./ShippingProtection\";\nimport HelpCircle from \"./HelpCircle\";\n\nexport const icons = {\n Truck,\n Locate,\n Store,\n Package,\n Clock,\n Phone,\n CheckMark,\n PlaceholderThumbnail,\n ArrowRight,\n Exchange,\n PlaceholderPictures,\n OriginalPayment,\n GiftCard,\n Reshop,\n StoreCredit,\n ShippingLabel,\n Printerless,\n NarvarBadge,\n Favorite,\n CheckCircle,\n Error,\n ShippingProtection,\n HelpCircle,\n};\n\nconst Icon = ({\n icon,\n ...props\n}: { icon: IconMap } & SVGProps) => icons[icon](props);\n\nexport default Icon;\n\nexport type { IconMap };\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst Truck: IconType = (props) => (\n \n);\n\nexport default Truck;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst Locate: IconType = (props) => (\n \n);\n\nexport default Locate;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst Store: IconType = (props) => (\n \n);\n\nexport default Store;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst Package: IconType = (props) => (\n \n);\n\nexport default Package;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst Clock: IconType = (props) => (\n \n);\n\nexport default Clock;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst Phone: IconType = (props) => (\n \n);\n\nexport default Phone;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst CheckMark: IconType = (props) => (\n \n);\n\nexport default CheckMark;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst PlaceholderThumbnail: IconType = (props) => (\n \n);\n\nexport default PlaceholderThumbnail;\n","import * as React from \"react\";\nimport { IconType } from \"./types\";\n\nconst ArrowRight: IconType = (props) => (\n \n);\n\nexport default ArrowRight;\n","import * as React from \"react\";\nimport { IconType } from \"./types\";\n\nconst Exchange: IconType = (props) => (\n \n);\n\nexport default Exchange;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst PlaceholderPictures: IconType = (props) => (\n \n);\n\nexport default PlaceholderPictures;\n","import * as React from \"react\";\nimport { IconType } from \"./types\";\n\nconst OriginalPayment: IconType = (props) => (\n \n);\n\nexport default OriginalPayment;\n","import * as React from \"react\";\nimport { IconType } from \"./types\";\n\nconst GiftCard: IconType = (props) => (\n \n);\n\nexport default GiftCard;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst Reshop: IconType = (props) => (\n \n);\n\nexport default Reshop;\n","import * as React from \"react\";\nimport { IconType } from \"./types\";\n\nconst StoreCredit: IconType = (props) => (\n \n);\n\nexport default StoreCredit;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst ShippingLabel: IconType = (props) => (\n \n);\nexport default ShippingLabel;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst Printerless: IconType = (props) => (\n \n);\nexport default Printerless;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst NarvarBadge: IconType = (props) => (\n \n);\n\nexport default NarvarBadge;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst Favorite: IconType = (props) => (\n \n);\n\nexport default Favorite;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst CheckCircle: IconType = (props) => (\n \n);\n\nexport default CheckCircle;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst Error: IconType = (props) => (\n \n);\n\nexport default Error;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst ShippingProtection: IconType = (props) => (\n \n);\n\nexport default ShippingProtection;\n","import React from \"react\";\nimport { IconType } from \"./types\";\n\nconst HelpCircle: IconType = (props) => (\n \n);\n\nexport default HelpCircle;\n","import { useCallback } from \"react\";\nimport { useMutation, gql } from \"@apollo/client\";\nimport { useRequestStatus } from \"../../shared/modules/hooks\";\nimport { memoObjectByKeyValues } from \"../../shared/modules/object\";\n\nconst CHANGE_REFUND_METHOD = gql`\n mutation changeRefundMethod(\n $id: String!\n $refundMethod: String!\n $note: String\n ) {\n changeRefundMethod(\n returnHashid: $id\n refundMethod: $refundMethod\n note: $note\n ) {\n success\n }\n }\n`;\n\nexport function useChangeRefundMethod() {\n const [mutate, state] = useMutation(CHANGE_REFUND_METHOD);\n const status = useRequestStatus(state);\n const changeRefundMethod = useCallback(\n (variables) => mutate({ variables }),\n [mutate],\n );\n\n return memoObjectByKeyValues({\n changeRefundMethod,\n status,\n });\n}\n","import {\n compose,\n curry,\n filter,\n find,\n first,\n flatten,\n fromPairs,\n get,\n getOr,\n groupBy,\n identity,\n intersection,\n map,\n max,\n merge,\n omit,\n range,\n reduce,\n set,\n sortBy,\n stubTrue,\n sumBy,\n uniq,\n} from \"lodash/fp\";\nimport { useMemo, useState } from \"react\";\n\nimport { getTranslation } from \"../../shared/modules/config\";\nimport { getReshopCancelError } from \"../../shared/modules/decodeError\";\nimport { toggleContainerClass } from \"../../shared/modules/frame\";\nimport locale from \"../../shared/modules/lang\";\nimport {\n memoObjectByKeyValues,\n mergeRequestStatus,\n renameKeys,\n useMergeRequestStatus,\n} from \"../../shared/modules/object\";\nimport { simplifyReturnStatus } from \"../../shared/modules/returnStatuses\";\nimport { interpolate } from \"../../shared/modules/template\";\nimport { config } from \"../config\";\nimport {\n useOrderFetchData,\n useReturnStepsState,\n} from \"../contexts/returnSteps\";\nimport { useCancelReturn } from \"../data/cancelReturn\";\nimport { useChangeRefundMethod } from \"../data/changeRefundMethod\";\nimport useEvenExchangeProduct from \"../data/evenExchangeProduct\";\nimport useCustomer from \"../hooks/useCustomer\";\nimport useLoginSession from \"../hooks/useLoginSession\";\nimport Sparsem, {\n UNAVAILABLE_IN_ANY_SET,\n UNAVAILABLE_IN_THIS_SET,\n} from \"./sparsem\";\nimport useClaims from \"../hooks/useClaims\";\n\nexport const hasExchangeForShopNowCredit = (items) =>\n items.some((item) => item.isStoreCredit);\n\nexport const buildReturnItem = (item, lineItem, returnReasonsById = {}) => {\n const { comment, quantity, pictures } = item;\n const childReturnReasonId = get(\"returnReason\", item.childReturnReason);\n const childReturnReason = childReturnReasonId\n ? returnReasonsById[childReturnReasonId].reasonTitle\n : undefined;\n\n return {\n comment,\n quantity,\n pictures,\n returnReason: returnReasonsById[item.returnReason].reasonTitle,\n returnReasonId: item.returnReason,\n childReturnReason,\n childReturnReasonId,\n lineItemId: lineItem.id,\n };\n};\n\nexport const buildExchangeItem = (item, lineItem, returnReasonsById) => {\n const { exchange } = item;\n const returnItem = buildReturnItem(item, lineItem, returnReasonsById);\n\n return {\n returnItem,\n newProductId: exchange.productId,\n newProductVariantId: exchange.variantId,\n newProductVariantInfo: exchange.variantInfo,\n newProductSku: exchange.sku,\n newProductName: exchange.name,\n newProductImage: exchange.productImageUrl,\n newProductPriceAmount: exchange.priceAmount,\n newProductPriceCurrency: exchange.priceCurrency,\n };\n};\n\nexport const buildItemsByType = (items, lineItemsById, returnReasonsById) => {\n const returnItems = items\n .filter((item) => item.type === \"return\")\n .map((item) =>\n buildReturnItem(item, lineItemsById[item.id], returnReasonsById),\n );\n\n let exchangeItems = items\n .filter((item) => item.type === \"exchange\")\n .map((item) =>\n // Due to backend limitation, we convert a exchange with quantity > 1\n // into multiple exchanges which quantity is 1\n range(0, item.quantity).map(() =>\n buildExchangeItem(\n set(\"quantity\", 1, item),\n lineItemsById[item.id],\n returnReasonsById,\n ),\n ),\n );\n exchangeItems = flatten(exchangeItems);\n\n return { returnItems, exchangeItems };\n};\n\n// build carousel items (swiper items), if there are exchange items selected and\n// that line item still have returnable quantity, create one more carousel item\n// for user to select. (item.id, item.localId) are used to distinguish different\n// carousel items\nexport const buildCarouselItems = (orderItems, selected) =>\n orderItems.reduce((remapped, item) => {\n let matching = selected.filter((i) => i.id === item.id);\n matching = sortBy(\"localId\", matching);\n const returnItem = matching.find((i) => i.type === \"return\");\n let exchangeItems = matching.filter((i) => i.type === \"exchange\");\n\n if (exchangeItems.length) {\n // the sum of quantity user selected for exchange and return\n const returningQuantity =\n sumBy(getOr(0, \"quantity\"), exchangeItems) +\n (returnItem?.quantity ?? 0);\n const returnableQuantity = item.returnableQuantity - returningQuantity;\n exchangeItems = exchangeItems.map((i) => ({\n ...item,\n localId: i.localId,\n returnableQuantity: returnableQuantity + (i.quantity ?? 0),\n }));\n\n let additionalCarouselItems = [];\n if (returnableQuantity || returnItem) {\n const maxExId = max(exchangeItems.map(get(\"localId\")));\n const localId = returnItem?.localId || maxExId + 1;\n additionalCarouselItems.push({\n ...item,\n localId,\n returnableQuantity: returnableQuantity + (returnItem?.quantity ?? 0),\n });\n }\n\n return [...remapped, ...exchangeItems, ...additionalCarouselItems];\n } else {\n // item is not selected or only selected for return, keep the existing\n // localId or default set to 1\n const localId = returnItem?.localId || 1;\n return [...remapped, { ...item, localId }];\n }\n }, []);\n\nexport const buildVariantInfoMap = (variantInfo) =>\n variantInfo.reduce((result, variant) => {\n result[variant.name] = variant.value;\n return result;\n }, {});\n\nexport const getVariantOption = (name, options) =>\n options.find((o) => o.name === name);\n\nexport const getMostSimilarVariantsForSelection = (\n variants,\n variantsOptions,\n selection,\n) => {\n const filterByOption = (arr, optionName) => {\n return arr.filter(\n (v) => v.variantInfoMap[optionName] === selection[optionName],\n );\n };\n\n const reduced = variantsOptions.reduce((result, option) => {\n const filtered = filterByOption(result, option.name);\n return filtered.length ? filtered : result;\n }, variants);\n\n return reduced;\n};\n\nexport const buildVariantName = (option) => `option${option.position}`;\n\nexport const buildVariantAvailaibityByValue = (\n variantOption,\n currentSelection,\n itemDetails,\n) => {\n const { position, values } = variantOption;\n // Names of all previous variants\n const names = itemDetails.options\n .slice(0, position - 1)\n .map(({ name }) => name);\n const filtered = itemDetails.variantInfo.filter(\n (v) =>\n v.availableForSale &&\n // Filter variants which value matches user selection\n names.every((n) => {\n if (!currentSelection[n]) return true;\n const option = getVariantOption(n, itemDetails.options);\n if (!option) return false;\n return v[buildVariantName(option)] === currentSelection[n];\n }),\n );\n\n if (!filtered.length) return;\n\n const optionName = buildVariantName(variantOption);\n\n return values.reduce((result, val) => {\n result[val] = filtered.some((v) => v[optionName] === val);\n return result;\n }, {});\n};\n\nexport const buildVariants = (details) =>\n details.variantInfo.map((v) => {\n const { variantInfo, variantInfoMap } = details.options.reduce(\n (result, option) => {\n const { name } = option;\n const value = v[buildVariantName(option)];\n result.variantInfo.push({ name, value });\n result.variantInfoMap[name] = value;\n return result;\n },\n { variantInfo: [], variantInfoMap: {} },\n );\n\n return {\n variantInfo,\n variantInfoMap,\n available: v.availableForSale,\n displayPrice: details.displayPrice,\n displayName: v.displayName,\n availabilityStatus: v.availabilityStatus,\n // id: details.id,\n name: v.displayName || v.option0 || details.name,\n productImageAltTxt: v.imageAlt || details.productImageAltTxt,\n productImageUrl: v.imageSrc || details.productImageUrl,\n variantId: v.variantId,\n productId: v.productId,\n sku: v.sku,\n option0: v.option0,\n priceAmount: v.priceAmount,\n priceCurrency: v.priceCurrency,\n priceFormatted: v.priceFormatted,\n hideDisplayPrice: details.hideDisplayPrice,\n };\n });\n\nexport const buildVariantsOptionsFallback = (item) =>\n item.variantInfo.map((i, index) => ({\n name: i.name,\n position: index + 1,\n values: [i.value],\n }));\n\nexport const createCarouselItemKey = (item) => `${item.id}_${item.localId}`;\n\nexport const isSameCarouselItem = (itemA, itemB) =>\n itemA.id === itemB.id && itemA.localId === itemB.localId;\n\nexport const rebuildVariantSelection = (name, value, selection, variants) => {\n const newVariants = { ...selection, [name]: value };\n const availableVariants = variants.filter(\n (v) => v.available && v.variantInfoMap[name] === value,\n );\n\n if (availableVariants.length) {\n const variantNames = Object.keys(newVariants);\n const availabilityByVariant = variantNames.reduce((result, n) => {\n result[n] = availableVariants.filter(\n (v) => v.variantInfoMap[n] === newVariants[n],\n );\n return result;\n }, {});\n const availabilityCounter = availableVariants.map((v) =>\n variantNames.reduce((counter, n) => {\n if (availabilityByVariant[n].includes(v)) return counter + 1;\n return counter;\n }, 0),\n );\n const maxCounter = max(availabilityCounter);\n const maxVariantIndex = availabilityCounter.indexOf(maxCounter);\n const maxVariant = availableVariants[maxVariantIndex];\n\n return variantNames.reduce((result, n) => {\n const val = maxVariant.variantInfoMap[n];\n if (val) result[n] = val;\n return result;\n }, {});\n }\n\n return newVariants;\n};\n\nexport const mapVariantInfo = (variantInfoArray, options) =>\n fromPairs(\n variantInfoArray.map((v, i) => [\n v.name,\n {\n optionId: v.name,\n optionName: v.name,\n optionPosition:\n v.position ??\n options?.find((opt) => opt.name === v.name)?.position ??\n i,\n value: v.value,\n name: v.value,\n },\n ]),\n );\n\nexport const mapShipmentActionStatus = (status) => {\n return status?.error\n ? \"error\"\n : status?.loading\n ? \"loading\"\n : status?.data\n ? \"success\"\n : undefined;\n};\n\nexport const newSelectedItemToState = (item) => {\n const parentId = item.reason.parent || item.reason.id;\n const childId = item.reason.parent ? item.reason.id : null;\n const parentReason = find({ id: parentId }, item.availableReasons);\n const childReason = find({ id: childId })(item.availableReasons);\n const isShopNow = item.type === \"exchangeForCredit\";\n const isClaim = item.type === \"claim\";\n return {\n localId: item.localId,\n id: item.itemId,\n sku: item._sku, // sku can be missing on nth's state, use the private attribute _sku instead.\n // claim and shop now credit are treated as return in backend, use\n // `isStoreCredit` and `isClaim` to distinguish them in UI\n type: isShopNow || isClaim ? \"return\" : item.type,\n isStoreCredit: isShopNow,\n isClaim,\n eligible: !!parentReason?.eligible,\n ineligibleReason: parentReason?.ineligibleReason ?? [],\n returnReason: parentReason?.id,\n childReturnReason: childReason\n ? {\n eligible: !!childReason.eligible,\n ineligibleReason: childReason.ineligibleReason ?? [],\n returnReason: childReason.id,\n }\n : null,\n quantity: item.quantity,\n pictures: item.pictures ?? [],\n comment: item.customerComments,\n attested: item.attested ?? false, // TODO: confirm the attribute name with backend\n exchange:\n item.exchangeTo && !isShopNow\n ? {\n ...item.exchangeTo,\n sku: item.exchangeTo._sku, // sku can be missing on nth's state, use the private attribute _sku instead.\n variantInfo: Object.values(item.exchangeTo.variantInfo ?? {}).map(\n (v) => ({ name: v.optionId, value: v.value }),\n ),\n }\n : undefined,\n };\n};\n\nexport const eventDataToNewSelectedItem = (value) => {\n let { item, selectedReason, ...others } = value;\n return {\n ...item,\n ...others,\n reason: selectedReason,\n };\n};\n\n// TRICKS: an constant empty array to avoid irrelevant rerender. Without it, it\n// cases the `useMemo` of components, and hooks (eg. use-item-state,\n// use-item-selectors), who depends on ineligibleReasons, to rerender every\n// time.\nconst EMPTY_ARRAY = [];\nexport const itemIneligibleReasons = (item) => {\n if (item.someReasonsEligible) {\n return EMPTY_ARRAY;\n }\n\n const matrixIntersection = (ary) =>\n reduce(intersection, ary && ary[0], ary) || EMPTY_ARRAY;\n const reasonsMatrix = item.eligibilityCriteria.map(\n (ec) => ec.ineligibleReason,\n );\n return uniq(matrixIntersection(reasonsMatrix));\n};\n\nexport const useNthAllItems = ({ localItems }) => {\n const orderFetchData = useOrderFetchData();\n const order = orderFetchData.state.data?.order;\n const isNotInsurancePolicy = (item) =>\n item.sku !== \"NARVARDELIVERYPROTECTION\";\n\n return useMemo(\n () =>\n localItems.filter(isNotInsurancePolicy).map((item) => {\n const sortByReasonPriority = sortBy(\n (criteria) =>\n orderFetchData?.state?.data?.returnReasonsById?.[\n criteria.returnReason\n ]?.priority,\n );\n const validCriteria = (criteria) =>\n !criteria.returnReasonParent ||\n item.eligibilityCriteria.some(\n (parent) => parent.returnReason === criteria.returnReasonParent,\n );\n const getClaimIneligibleReason = compose(\n first,\n uniq,\n identity,\n flatten,\n map((criteria) => criteria.ineligibleClaimReason),\n );\n let ret = compose(\n config.showItemSku ? identity : omit(\"sku\"), // show SKU only if the feature flag is enabled\n merge({\n _sku: item.sku, // rename the sku attribute for internal use, because it can be removed when show_item_sku is disabled.\n price: item.hideDisplayPrice\n ? undefined\n : {\n value:\n item.priceAmount * order.presentmentCurrencySubunitToUnit,\n currency: order.presentmentCurrency,\n },\n eligibilities: {\n return: {\n isEligible: item.allowReturn,\n message:\n !item.allowReturn && !item.allowExchange\n ? itemIneligibleReasons(item).join(\" \")\n : undefined,\n },\n exchange: {\n isEligible: item.allowExchange,\n },\n repair: {\n isEligible: false,\n },\n claims: {\n isEligible: item.allowClaim,\n message: item.allowClaim\n ? undefined\n : getClaimIneligibleReason(item.eligibilityCriteria),\n },\n },\n availableReasons: compose(\n map((criteria) => ({\n ...criteria,\n id: criteria.returnReason,\n name: criteria.returnReasonTitle,\n childIds: criteria.returnReasonParent\n ? null\n : compose(\n map((c) => c.returnReason),\n sortByReasonPriority,\n filter(\n (c) => criteria.returnReason === c.returnReasonParent,\n ),\n )(item.eligibilityCriteria),\n })),\n sortByReasonPriority,\n filter(validCriteria),\n )(item.eligibilityCriteria),\n variantInfo: mapVariantInfo(item.variantInfo),\n }),\n renameKeys({\n id: \"itemId\",\n productTitle: \"name\",\n productImageUrl: \"imageUrl\",\n returnableQuantity: \"remainingQuantity\",\n variantInfo: \"variantInfoOriginal\",\n hasClaimProtection: \"hasShippingProtection\",\n }),\n )(item);\n\n return ret;\n }),\n [orderFetchData, order, localItems],\n );\n};\n\nexport const useNthExchangeProps = ({ selectingItem }) => {\n const orderFetchData = useOrderFetchData();\n const order = orderFetchData.state.data?.order;\n const state = useReturnStepsState();\n const [exchangeVariantInfo, setExchangeVariantInfo] = useState();\n\n const hideExchangeOptions =\n (config.isReturnUpsellDisabled && state.selectingItem?.type === \"return\") ||\n state.selectingItem?.isClaim;\n const exchangeCurrency = order?.presentmentCurrency;\n const { result: exchangeResult, status } = useEvenExchangeProduct({\n query: selectingItem?.name ?? \"\",\n productId: selectingItem?.productId ?? \"\",\n variantId: selectingItem?.variantId ?? \"\",\n price: selectingItem?.priceAmount ?? 0,\n compareAtPrice: selectingItem?.compareAtPriceAmount,\n displayPrice: selectingItem?.displayPrice,\n currency: exchangeCurrency,\n locale,\n countryCode: order?.fromCountryCode,\n showProduct: true,\n });\n\n const exchangeVariants = useMemo(\n () =>\n exchangeResult && !hideExchangeOptions\n ? buildVariants(exchangeResult).map(({ sku, ...v }) => ({\n ...v,\n _sku: sku, // rename the sku attribute for internal use, because it can be removed when show_item_sku is disabled.\n price: {\n value: v.priceAmount * order.presentmentCurrencySubunitToUnit,\n currency: exchangeCurrency,\n },\n imageUrl: v.productImageUrl,\n variantInfo: mapVariantInfo(v.variantInfo, exchangeResult.options),\n }))\n : [],\n [exchangeResult, hideExchangeOptions],\n );\n const exchangeOptions = useMemo(() => {\n if (!exchangeResult?.options || hideExchangeOptions) return [];\n\n // setup sparsem availability check\n const sparsem = new Sparsem();\n const sparsemVariants = exchangeVariants\n .filter((v) => v.available)\n .map((v) => {\n let values = compose(\n (infos) => infos.map((info) => info.value),\n // map(get(\"value\")),\n sortBy(\"optionPosition\"),\n )(Object.values(v.variantInfo || {}));\n return values;\n });\n const sparsemOptions = sortBy(\"position\", exchangeResult.options).map(\n (opt) => opt.values,\n );\n sparsem.setArr(sparsemVariants, sparsemOptions);\n\n return exchangeResult.options.map((opt) => {\n // When there are no available variants of a product (completely missing\n // in the array), we hide the option from the exchange swatches. It is\n // meaningless to show it on UI, because we don't know the product image,\n // there are no `availabilityStatus` we can show and no available variants\n // as well. We only filter this for \"Product\" option, because consumer may\n // still want to know other out of stock options (e.g. all sizes including\n // those were out of stock)\n const filterUnavailableOption = (v) =>\n exchangeResult.variantInfo.some(\n (info) => info[`option${opt.position}`] === v,\n );\n let values = opt.values\n .filter(opt.name === \"Product\" ? filterUnavailableOption : stubTrue)\n .map((v, i) => ({\n name: v,\n value: v,\n position: i,\n }));\n if (config.sortExchangeVariantsByAvailability) {\n // query the availability of different values under the current option and exchange selections\n const sparsemQuery = exchangeResult.options.map((o) =>\n o.name === opt.name\n ? null\n : (exchangeVariantInfo?.[o.name]?.value ?? null),\n );\n const sparsemResult = sparsem.search(sparsemQuery);\n const availability = values.reduce((acc, v) => {\n acc[v.value] =\n sparsemResult[v.value] === UNAVAILABLE_IN_THIS_SET ||\n sparsemResult[v.value] == UNAVAILABLE_IN_ANY_SET;\n return acc;\n }, {});\n values = compose(\n (vs) => vs.map((v, i) => set(\"position\", i, v)),\n sortBy((v) => availability[v.value]),\n )(values);\n }\n return {\n ...opt,\n id: opt.name,\n displayType: /^product$/i.test(opt.name) ? \"image\" : undefined,\n values,\n };\n });\n }, [\n hideExchangeOptions,\n config.sortExchangeVariantsByAvailability,\n exchangeResult,\n exchangeVariants,\n exchangeVariantInfo,\n ]);\n\n return memoObjectByKeyValues({\n exchangeVariants,\n exchangeOptions,\n status,\n setExchangeVariantInfo,\n });\n};\n\nconst isReasonTypeMatched = curry(\n (selectingItem, reason) => selectingItem.isClaim === !!reason.isClaim,\n);\n\nconst isEligibleReason = curry((selectingItem, reason) => {\n if (selectingItem.isClaim) {\n // selectingItem.type of claim is \"return\", check isClaim indicator instead\n return reason.isEligibleForClaim;\n }\n\n // For normal reason, we always return true. The option is not hide from UI,\n // instead the ineligible reason will be shown when user click the next\n // button. Retailer may use that to ask user to contact them directly or route\n // them to a different page/form.\n return true;\n});\n\nconst hasEligibleChildReason = (reason, index, array) => {\n if ((reason.childIds ?? []).length === 0) return true;\n return reason.childIds.some((childId) => array.find((r) => r.id === childId));\n};\n\nconst filterAvailableChildIds = curry((availableReasons, reason) => ({\n ...reason,\n childIds: (reason.childIds ?? []).filter((childId) =>\n availableReasons.find((r) => r.id === childId),\n ),\n}));\n\nexport const useReturnReasonOrderItem = ({ newSelectingItem }) => {\n const state = useReturnStepsState();\n const nonEmptyCart = state.items.length > 0;\n const { hasClaimItems, claimReasonCode } = useClaims();\n return useMemo(() => {\n let ret = newSelectingItem;\n\n // no mix cart for claim, we only support one claim type (reason) per return cart.\n if (nonEmptyCart) {\n if (hasClaimItems) {\n ret = set(\"eligibilities.return.isEligible\", false, ret);\n ret = set(\"eligibilities.exchange.isEligible\", false, ret);\n ret = {\n ...ret,\n availableReasons: ret.availableReasons?.filter(\n (reason) =>\n reason.id === claimReasonCode ||\n reason.childIds?.includes(claimReasonCode),\n ),\n };\n } else {\n ret = set(\"eligibilities.claims.isEligible\", false, ret);\n ret = set(\n \"eligibilities.claims.message\",\n config.translations.choose_items_claim_mix_cart_error,\n ret,\n );\n }\n }\n\n // new item selection, type is not chosen yet\n if (!state.selectingItem?.type) return ret;\n\n // filter available reasons based on the selecting item type and reason type\n let availableReasons = ret.availableReasons\n .filter(isReasonTypeMatched(state.selectingItem))\n .filter(isEligibleReason(state.selectingItem))\n .filter(hasEligibleChildReason);\n availableReasons = availableReasons.map(\n filterAvailableChildIds(availableReasons),\n );\n return {\n ...ret,\n availableReasons,\n };\n }, [\n newSelectingItem,\n state.selectingItem,\n nonEmptyCart,\n hasClaimItems,\n claimReasonCode,\n ]);\n};\n\nexport const useNthSubmittedReturns = ({ allItems }) => {\n const orderFetchData = useOrderFetchData();\n const order = orderFetchData.state.data?.order;\n const returnHistory = orderFetchData.state.data?.returnHistory;\n const loginSession = useLoginSession();\n const customer = useCustomer();\n const { cancelReturn, cancelStatus } = useCancelReturn();\n const { changeRefundMethod, status: changeRefundMethodStatus } =\n useChangeRefundMethod();\n\n const [returnAction, setReturnAction] = useState();\n\n const returnActionStatus = useMemo(() => {\n const { returnId } = returnAction ?? {};\n if (!returnId) return;\n\n const reshopCancelError = getReshopCancelError(cancelStatus.error);\n const reshopCancelErrorMessage =\n reshopCancelError &&\n interpolate(\n config.translations.choose_items_rma_action_reshop_cancel_error,\n { refund_id: reshopCancelError.reshop_refund_id },\n );\n\n if (returnAction.action === \"cancel\") {\n const status = mapShipmentActionStatus(cancelStatus);\n const message =\n status === \"error\"\n ? reshopCancelError\n ? reshopCancelErrorMessage\n : config.translations.choose_items_rma_action_cancel_error\n : undefined;\n\n return { returnId, status, message };\n }\n if (returnAction.action === \"changeRefundMethod\") {\n const status = mapShipmentActionStatus(changeRefundMethodStatus);\n const message = reshopCancelError\n ? reshopCancelErrorMessage\n : changeRefundMethodStatus.error?.message;\n return { returnId, status, message };\n }\n }, [returnAction, cancelStatus, changeRefundMethodStatus]);\n\n const submittedReturns = useMemo(\n () =>\n returnHistory.map((history) => {\n const convertLineItem = (item) =>\n compose(\n set(\"itemId\", `gid://shopify/LineItem/${item.itemId}`),\n merge(\n allItems.find(\n (localItem) =>\n localItem.itemId === `gid://shopify/LineItem/${item.itemId}`,\n ) ?? {},\n ),\n merge({\n reason: {\n id: item.childReturnReasonCode ?? item.returnReasonCode,\n name: item.returnReason,\n },\n }),\n renameKeys({\n itemName: \"name\",\n }),\n )(item);\n\n // Since we will split exchange line item to multiple return line items,\n // we need to group it back.\n const groupSplittedLineItem = (items) => {\n const ret = compose(\n map((itemsWithSameId) => ({\n ...itemsWithSameId[0],\n quantity: sumBy(\"quantity\", itemsWithSameId),\n })),\n groupBy(\"itemId\"),\n )(items);\n return ret;\n };\n\n const returnLabels = [];\n const returnShipmentActions = [];\n\n if (history.returnLabel) {\n returnLabels.push({\n dataUrl: null,\n labelType: \"PACKING_SLIP_AND_SHIPPING_LABEL\",\n labelUrl: history.returnLabel,\n mimeType: \"application/pdf\",\n });\n returnShipmentActions.push({\n title: getTranslation(\"choose_items_rma_action_reprint_labels\"),\n href: history.returnLabel,\n });\n }\n\n if (history.qrCodeUrl) {\n returnLabels.push({\n dataUrl: null,\n labelType: \"QR_CODE\",\n labelUrl: history.qrCodeUrl,\n mimeType: \"image/png\",\n });\n returnShipmentActions.push({\n title: getTranslation(\"choose_items_rma_action_reprint_qr_code\"),\n href: history.qrCodeUrl,\n });\n }\n\n const isMultiLabel = history.returnShipments.length > 1;\n history.returnShipments?.forEach((ship) => {\n if (ship.returnTrackingLink) {\n returnShipmentActions.push({\n title: isMultiLabel\n ? interpolate(\n getTranslation(\n \"choose_items_rma_action_track_return_multi_label\",\n ),\n {\n letter: ship.letter,\n tracking_number: ship.returnTrackingNumber,\n },\n )\n : getTranslation(\"choose_items_rma_action_track_return\"),\n href: ship.returnTrackingLink,\n });\n }\n });\n\n if (\n history.refundMethod === \"reshop\" &&\n [\n \"initiated\",\n \"on_its_way_to_retailer\",\n \"delivered_to_retailer\",\n \"received_by_retailer\",\n ].includes(history.currentStatus)\n ) {\n returnShipmentActions.push({\n title: getTranslation(\n \"choose_items_rma_action_change_refund_method_original_payment\",\n ),\n onClick: () => {\n setReturnAction({\n returnId: history.id,\n action: \"changeRefundMethod\",\n });\n changeRefundMethod({\n id: history.id,\n refundMethod: \"original_payment\",\n note: \"\",\n })\n .then((result) => {\n loginSession.logout();\n\n if (config.isNthLoginUi) {\n toggleContainerClass(\"nvo_container--new-login\");\n toggleContainerClass(\"nvo_container--logged-in\");\n }\n })\n .catch((error) => {\n console.error(\"error\", error);\n });\n },\n status:\n returnAction?.returnId === history.id &&\n returnAction?.action === \"changeRefundMethod\"\n ? returnActionStatus\n : undefined,\n });\n }\n\n if (history.cancellable) {\n returnShipmentActions.push({\n title: getTranslation(\"choose_items_rma_action_cancel_return\"),\n onClick: () => {\n setReturnAction({\n returnId: history.id,\n action: \"cancel\",\n });\n cancelReturn({\n orderNumber: order.orderNumber,\n email: customer.email,\n returnId: history.id,\n })\n .then((result) => {\n loginSession.logout();\n\n if (config.isNthLoginUi) {\n toggleContainerClass(\"nvo_container--new-login\");\n toggleContainerClass(\"nvo_container--logged-in\");\n }\n })\n .catch((error) => {\n console.error(\"error\", error);\n });\n },\n status:\n returnAction?.returnId === history.id &&\n returnAction?.action === \"cancel\"\n ? returnActionStatus\n : undefined,\n });\n }\n\n const ret = compose(\n merge({\n displayStatus: getTranslation(\n `return_status_simplified_to_consumer_${simplifyReturnStatus(\n history.currentStatus,\n )}`,\n ),\n canBeCancelled: history.cancellable,\n returnShipments: [\n {\n returnLabels,\n items: groupSplittedLineItem(\n history.returnItems.map(convertLineItem),\n ),\n returnShipmentActions,\n returnTracking: history.carrierTrackingNumber\n ? {\n carrierMoniker: history.returnMethod,\n carrierName: history.returnMethod,\n narvarTrackingUrl: `?rid=${history.id}&tracking_number=${history.carrierTrackingNumber}`,\n trackingNumber: history.carrierTrackingNumber,\n }\n : undefined,\n },\n ],\n }),\n renameKeys({\n currentStatus: \"status\",\n returnShipments: \"returnShipmentsRaw\",\n }),\n )(history);\n\n return ret;\n }),\n [returnHistory, allItems, returnAction, returnActionStatus],\n );\n\n return memoObjectByKeyValues({\n submittedReturns,\n returnActionStatus,\n cancelStatus,\n });\n};\n\nexport const useInventoryCheck = ({ items }) => {\n const orderFetchData = useOrderFetchData();\n const order = orderFetchData.state.data?.order;\n const currency = order?.presentmentCurrency;\n const countryCode = order?.fromCountryCode;\n\n const hooks = items.map((item) =>\n useEvenExchangeProduct({\n query: item?.productTitle ?? item?.name ?? \"\",\n productId: item?.productId ?? \"\",\n variantId: item?.variantId ?? \"\",\n price: item?.priceAmount ?? 0,\n compareAtPrice: item?.compareAtPriceAmount,\n displayPrice: item?.displayPrice,\n currency: currency,\n locale,\n countryCode: countryCode,\n showProduct: true,\n }),\n );\n const exchangeResults = hooks.map((hook) => hook.result);\n const statuses = hooks.map((hook) => hook.status);\n const status = useMergeRequestStatus(...statuses);\n\n const { results, resultsById } = useMemo(() => {\n if (status.loading) return {};\n\n const matchingVariants = exchangeResults.map((result, i) => {\n const variantId = items[i]?.variantId;\n const matchingVariant = find(\n { variantId: variantId },\n result?.variantInfo ?? [],\n );\n return [\n variantId,\n matchingVariant ?? { variantId: variantId, availableForSale: false },\n ];\n });\n return {\n results: matchingVariants.map(get(1)),\n resultsById: fromPairs(matchingVariants),\n };\n }, [status, ...items, ...exchangeResults]);\n\n const allItemsAvailableForSale = results.every(\n (result) => result.availableForSale,\n );\n\n return memoObjectByKeyValues({\n status,\n results,\n resultsById,\n allItemsAvailableForSale,\n });\n};\n","import React, { useMemo, forwardRef } from \"react\";\nimport PropTypes from \"prop-types\";\nimport { compose, map, toPairs, fromPairs, isNumber } from \"lodash/fp\";\nimport { makeStyles } from \"@material-ui/core/styles\";\nimport InputAdornment from \"@material-ui/core/InputAdornment\";\nimport Typography from \"@material-ui/core/Typography\";\nimport { NumericFormat } from \"react-number-format\";\n\nimport TextField from \"./TextField\";\nimport { getCurrencyFormat } from \"../../modules/currency\";\n\nconst useStyles = makeStyles(\n (theme) => ({\n adornment: {\n gap: theme.spacing(0.5),\n },\n }),\n { name: \"N0NumberField\" },\n);\n\nconst NumberField = forwardRef(\n (\n { type, unit, unitPosition, min, max, showLimit, inputProps, ...props },\n ref,\n ) => {\n const classes = useStyles();\n const currencyFormat = useMemo(\n () => (type === \"currency\" ? getCurrencyFormat(unit) : null),\n [type, unit],\n );\n if (currencyFormat) {\n unit = currencyFormat.symbol;\n unitPosition = \"start\";\n }\n const adornmentProps = useMemo(() => {\n let ret = {\n startAdornment: [],\n endAdornment: [],\n };\n\n if (max > 0 && showLimit) {\n ret.endAdornment.push(\n {`/ ${max}`},\n );\n }\n if (unit) {\n (unitPosition === \"start\" ? ret.startAdornment : ret.endAdornment).push(\n unit,\n );\n }\n\n ret = compose(\n fromPairs,\n map(([key, nodes]) => {\n const adornmentNode = (\n \n {nodes.map((node, i) => (\n {node}\n ))}\n \n );\n return [key, adornmentNode];\n }),\n toPairs,\n )(ret);\n\n return ret;\n }, [unit, unitPosition, max, showLimit]);\n\n const iProps = useMemo(\n () => ({\n min,\n max,\n ...inputProps,\n }),\n [min, max, inputProps],\n );\n\n if (type === \"currency\") {\n return (\n {\n props.onChange?.({\n target: {\n name: props.name,\n ...values,\n },\n });\n }}\n customInput={TextField}\n inputProps={iProps}\n {...adornmentProps}\n {...props}\n />\n );\n }\n\n return (\n \n );\n },\n);\nNumberField.displayName = \"NumberField\";\nNumberField.propTypes = {\n ...TextField.propTypes,\n type: PropTypes.oneOf([\"number\", \"currency\"]),\n unit: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),\n unitPosition: PropTypes.oneOf([\"start\", \"end\"]),\n min: PropTypes.number,\n max: PropTypes.number,\n showLimit: PropTypes.bool,\n};\n\nNumberField.defaultProps = {\n type: \"number\",\n unitPosition: \"end\",\n};\n\nexport default NumberField;\n","import { includes, keys } from \"lodash/fp\";\n\n// Return status simplified to consumer (e.g. shown on return history)\nexport const SIMPLIFIED_RETURN_STATUS = {\n out_of_stock_exception: \"initiated\",\n // delivered_but_not_received: -4, // TODO: need research\n exception: \"initiated\",\n // preauthorization: -1, // TODO: need research\n started: \"initiated\",\n requested: \"requested\",\n initiated: \"initiated\",\n on_its_way_to_retailer: \"on_its_way\",\n delivered_to_retailer: \"delivered\",\n received_by_retailer: \"received\",\n processing: \"received\",\n approved: \"approved\", // need timestamp\n rejected: \"declined\",\n refunded: \"refunded\", // need timestamp\n cancelled_by_retailer: \"cancelled\", // hide from the list\n cancelled_by_user: \"cancelled\", // hide from the list\n // refunded_and_exchanged: 10, // TODO: need research\n // refunded_return_awaiting_exchange_completion: 11, // TODO: need research\n // exchange_processed: 12, // TODO: need research\n resolve_manually_without_automation: \"initiated\",\n awaiting_claim_resolution: \"claim_reviewing\",\n claim_denied: \"declined\",\n\n _unknown: \"initiated\",\n};\n\nexport const simplifyReturnStatus = (status) =>\n SIMPLIFIED_RETURN_STATUS[status] ?? SIMPLIFIED_RETURN_STATUS._unknown;\n\nexport const RETURN_STATUSES = keys(SIMPLIFIED_RETURN_STATUS);\n\nexport const compareReturnStatus = (() => {\n const toNumber = (status) => RETURN_STATUSES.indexOf(status);\n const eq = (a, b) => toNumber(a) === toNumber(b);\n const gt = (a, b) => toNumber(a) > toNumber(b);\n const gte = (a, b) => toNumber(a) >= toNumber(b);\n const lt = (a, b) => toNumber(a) < toNumber(b);\n const lte = (a, b) => toNumber(a) <= toNumber(b);\n const memberOf = (a, statuses = []) => includes(a, statuses);\n return {\n eq,\n gt,\n gte,\n lt,\n lte,\n memberOf,\n };\n})();\n","import React, { FC } from \"react\";\n\nexport const countryCodeToCurrency: { [p: string]: string } = {\n AF: \"AFN\",\n AL: \"ALL\",\n DZ: \"DZD\",\n AS: \"USD\",\n AD: \"EUR\",\n AO: \"AOA\",\n AI: \"XCD\",\n AQ: \"USD\",\n AG: \"XCD\",\n AR: \"ARS\",\n AM: \"AMD\",\n AW: \"AWG\",\n AU: \"AUD\",\n AT: \"EUR\",\n AZ: \"AZN\",\n BS: \"BSD\",\n BH: \"BHD\",\n BD: \"BDT\",\n BB: \"BBD\",\n BY: \"BYN\",\n BE: \"EUR\",\n BZ: \"BZD\",\n BJ: \"XOF\",\n BM: \"BMD\",\n BT: \"BTN\",\n BO: \"BTN\",\n BQ: \"USD\",\n BA: \"BAM\",\n BW: \"BWP\",\n BV: \"NOK\",\n BR: \"BRL\",\n IO: \"USD\",\n BN: \"BND\",\n BG: \"BGN\",\n BF: \"XOF\",\n BI: \"BIF\",\n CV: \"CVE\",\n KH: \"KHR\",\n CM: \"XAF\",\n CA: \"CAD\",\n KY: \"KYD\",\n CF: \"XAF\",\n TD: \"XAF\",\n CL: \"CLF\",\n CN: \"CNY\",\n CX: \"AUD\",\n CC: \"AUD\",\n CO: \"COU\",\n KM: \"KMF\",\n CD: \"CDF\",\n CG: \"XAF\",\n CK: \"NZD\",\n CR: \"CRC\",\n HR: \"HRK\",\n CU: \"CUC\",\n CW: \"ANG\",\n CY: \"EUR\",\n CZ: \"CZK\",\n CI: \"CZK\",\n DK: \"DKK\",\n DJ: \"DJF\",\n DM: \"XCD\",\n DO: \"DOP\",\n EC: \"USD\",\n EG: \"EGP\",\n SV: \"USD\",\n GQ: \"XAF\",\n ER: \"ERN\",\n EE: \"EUR\",\n SZ: \"EUR\",\n ET: \"ETB\",\n FK: \"DKK\",\n FO: \"DKK\",\n FJ: \"FJD\",\n FI: \"EUR\",\n FR: \"EUR\",\n GF: \"EUR\",\n PF: \"XPF\",\n TF: \"EUR\",\n GA: \"XAF\",\n GM: \"GMD\",\n GE: \"GEL\",\n DE: \"EUR\",\n GH: \"GHS\",\n GI: \"GIP\",\n GR: \"EUR\",\n GL: \"DKK\",\n GD: \"XCD\",\n GP: \"EUR\",\n GU: \"USD\",\n GT: \"GTQ\",\n GG: \"GBP\",\n GN: \"GNF\",\n GW: \"XOF\",\n GY: \"GYD\",\n HT: \"USD\",\n HM: \"AUD\",\n VA: \"EUR\",\n HN: \"HNL\",\n HK: \"HKD\",\n HU: \"HUF\",\n IS: \"ISK\",\n IN: \"INR\",\n ID: \"IDR\",\n IR: \"XDR\",\n IQ: \"IQD\",\n IE: \"EUR\",\n IM: \"GBP\",\n IL: \"ILS\",\n IT: \"EUR\",\n JM: \"JMD\",\n JP: \"JPY\",\n JE: \"GBP\",\n JO: \"JOD\",\n KZ: \"KZT\",\n KE: \"KES\",\n KI: \"AUD\",\n KP: \"KPW\",\n KR: \"KRW\",\n KW: \"KWD\",\n KG: \"KGS\",\n LA: \"LAK\",\n LV: \"EUR\",\n LB: \"LBP\",\n LS: \"ZAR\",\n LR: \"LRD\",\n LY: \"LYD\",\n LI: \"CHF\",\n LT: \"EUR\",\n LU: \"EUR\",\n MO: \"MOP\",\n MG: \"MGA\",\n MW: \"MWK\",\n MY: \"MYR\",\n MV: \"MVR\",\n ML: \"XOF\",\n MT: \"EUR\",\n MH: \"USD\",\n MQ: \"EUR\",\n MR: \"MRU\",\n MU: \"MUR\",\n YT: \"EUR\",\n MX: \"MXV\",\n FM: \"RUB\",\n MD: \"MDL\",\n MC: \"EUR\",\n MN: \"MNT\",\n ME: \"EUR\",\n MS: \"XCD\",\n MA: \"MAD\",\n MZ: \"MZN\",\n MM: \"MMK\",\n NA: \"ZAR\",\n NR: \"AUD\",\n NP: \"NPR\",\n NL: \"EUR\",\n NC: \"XPF\",\n NZ: \"NZD\",\n NI: \"NIO\",\n NE: \"XOF\",\n NG: \"NGN\",\n NU: \"NZD\",\n NF: \"AUD\",\n MK: \"AUD\",\n MP: \"USD\",\n NO: \"NOK\",\n OM: \"OMR\",\n PK: \"PKR\",\n PW: \"USD\",\n PS: \"USD\",\n PA: \"USD\",\n PG: \"PGK\",\n PY: \"PYG\",\n PE: \"PEN\",\n PH: \"PHP\",\n PN: \"NZD\",\n PL: \"PLN\",\n PT: \"EUR\",\n PR: \"USD\",\n QA: \"QAR\",\n RO: \"RON\",\n RU: \"RUB\",\n RW: \"RWF\",\n RE: \"RWF\",\n BL: \"EUR\",\n SH: \"SHP\",\n KN: \"XCD\",\n LC: \"XCD\",\n MF: \"EUR\",\n PM: \"EUR\",\n VC: \"XCD\",\n WS: \"WST\",\n SM: \"EUR\",\n ST: \"STN\",\n SA: \"SAR\",\n SN: \"XOF\",\n RS: \"RSD\",\n SC: \"SCR\",\n SL: \"SLL\",\n SG: \"SGD\",\n SX: \"ANG\",\n SK: \"EUR\",\n SI: \"EUR\",\n SB: \"SBD\",\n SO: \"SOS\",\n ZA: \"ZAR\",\n GS: \"USD\",\n SS: \"SSP\",\n ES: \"EUR\",\n LK: \"LKR\",\n SD: \"SDG\",\n SR: \"SRD\",\n SJ: \"NOK\",\n SE: \"SEK\",\n CH: \"CHW\",\n SY: \"SYP\",\n TW: \"TWD\",\n TJ: \"TJS\",\n TZ: \"TZS\",\n TH: \"THB\",\n TL: \"USD\",\n TG: \"XOF\",\n TK: \"NZD\",\n TO: \"TOP\",\n TT: \"TTD\",\n TN: \"TND\",\n TR: \"TRY\",\n TM: \"TMT\",\n TC: \"USD\",\n TV: \"AUD\",\n UG: \"UGX\",\n UA: \"UAH\",\n AE: \"AED\",\n GB: \"GBP\",\n UM: \"USD\",\n US: \"USD\",\n UY: \"UYW\",\n UZ: \"UZS\",\n VU: \"VUV\",\n VE: \"VUV\",\n VN: \"VND\",\n VG: \"USD\",\n VI: \"USD\",\n WF: \"XPF\",\n EH: \"MAD\",\n YE: \"YER\",\n ZM: \"ZMW\",\n ZW: \"ZWL\",\n AX: \"EUR\",\n};\n\nexport const getCurrencyDP = (currency = \"USD\"): number => {\n const mapping: { [key: string]: number } = {\n BHD: 3,\n BIF: 0,\n CLF: 4,\n CLP: 0,\n DJF: 0,\n GNF: 0,\n IQD: 3,\n ISK: 0,\n JOD: 3,\n JPY: 0,\n KMF: 0,\n KRW: 0,\n KWD: 3,\n LYD: 3,\n OMR: 3,\n PYG: 0,\n RWF: 0,\n TND: 3,\n UGX: 0,\n UYI: 0,\n UYW: 4,\n VND: 0,\n VUV: 0,\n XAF: 0,\n XOF: 0,\n XPF: 0,\n };\n return mapping[currency] ?? 2;\n};\n\nexport const getCurrencyExponent = (currency = \"USD\"): number =>\n 10 ** getCurrencyDP(currency);\n\nexport type CurrencyProps = {\n value: number;\n locale?: string;\n currency?: string | null;\n includeCurrency?: boolean;\n needsExponent?: boolean;\n};\n\nexport const formatCurrency = ({\n value,\n locale = \"en-US\",\n currency = \"USD\",\n includeCurrency,\n needsExponent = true,\n}: CurrencyProps) => {\n const _locale = (locale || \"en_US\").replace(\"_\", \"-\");\n const country = _locale.split(\"-\")[1];\n const _currency = currency || countryCodeToCurrency[country] || \"USD\";\n const exponent = needsExponent ? getCurrencyExponent(_currency) : 1;\n\n const formatted = new Intl.NumberFormat(_locale, {\n style: \"currency\",\n currency: _currency,\n }).format(value / exponent);\n return includeCurrency ? `${formatted} ${_currency}` : formatted;\n};\n\nexport const Currency: FC = ({\n value,\n locale = \"en-US\",\n currency = \"USD\",\n includeCurrency,\n needsExponent = true,\n}) => (\n <>\n {formatCurrency({\n value,\n locale,\n currency,\n includeCurrency,\n needsExponent,\n })}\n >\n);\n","import React, { useMemo } from \"react\";\nimport PropTypes from \"prop-types\";\n\n/*\n * Flex box spacer or specify the width or height\n */\nfunction Spacer({ width, height }) {\n const styles = useMemo(\n () =>\n !width && !height\n ? { flex: 1 }\n : {\n width: width || \"100%\",\n height: height || \"100%\",\n },\n [width, height],\n );\n return ;\n}\n\nSpacer.propTypes = {\n width: PropTypes.number,\n height: PropTypes.number,\n};\n\nSpacer.defaultProps = {\n width: 0,\n height: 0,\n};\n\nexport default Spacer;\n","import React, { FC, ReactNode, CSSProperties } from \"react\";\nimport { Markup, MarkupProps } from \"interweave\";\n\nconst StyledMarkup: FC<\n Omit & {\n content?: ReactNode | null;\n style?: CSSProperties;\n }\n> = ({ content, style, noWrap, ...props }) => {\n if (React.isValidElement(content)) return content;\n if (typeof content !== \"string\") return null;\n\n if (props.tagName) return ;\n if (noWrap) return ;\n return (\n \n \n \n );\n};\n\nexport default StyledMarkup;\n","import React from \"react\";\nimport {\n ItemDisplay,\n AdditionalItemAttributeConfig,\n AdditionalItemAttributeTransformTypes,\n} from \"@narvar/nth-hook-loma\";\nimport { Currency, formatCurrency } from \"../transformers/Currency\";\n\nimport {\n AdditionalProperty,\n Maybe,\n AdditionalItemAttrs,\n Attribute,\n ExchangeVariant,\n ExtendedOrderItem,\n ExtendedReturnItem,\n ExtendedReturnItemInput,\n ExtendedSubmittedReturn,\n OfflineReturnsHandlingType,\n ReturnReasonsReturnType,\n} from \"@narvar/nth-kit-returns-headless\";\n\nexport const DEFAULT_ITEM_ATTR_CONFIG: string[] = [\n \"quantity\",\n \"variantInfo\",\n \"color\",\n \"size\",\n \"sku\",\n \"price\",\n];\n\nexport const filterItems = (\n items: ExtendedOrderItem[],\n submittedReturns: ExtendedSubmittedReturn[] = [],\n showOfflineReturnsAs: OfflineReturnsHandlingType = \"hidden\",\n): [ExtendedOrderItem[], ExtendedOrderItem[]] => {\n const eligible: ExtendedOrderItem[] = [];\n const ineligible: ExtendedOrderItem[] = [];\n const hasOfflineReturnItem = (item: ExtendedOrderItem) => {\n const onlineReturnedQuantity = submittedReturns\n .map((r) => r.returnShipments ?? [])\n .flat()\n .map((s) => s.items)\n .flat()\n .reduce(\n (sum, submittedReturnItem) =>\n submittedReturnItem.itemId === item.itemId\n ? sum + submittedReturnItem.quantity\n : sum,\n 0,\n );\n return (\n item.orderedQuantity - onlineReturnedQuantity > item.remainingQuantity\n );\n };\n\n items.forEach((item) => {\n const eligibility = item.eligibilities;\n if (\n (eligibility?.return?.isEligible || eligibility?.exchange?.isEligible) &&\n item.remainingQuantity > 0\n ) {\n eligible.push(item);\n } else if (\n item.remainingQuantity > 0 ||\n (showOfflineReturnsAs === \"ineligible\" && hasOfflineReturnItem(item))\n ) {\n ineligible.push(item);\n }\n });\n\n return [eligible, ineligible];\n};\n\nexport const splitItemsForMixReturns = (\n items?: ExtendedOrderItem[],\n selectedItems?: ExtendedReturnItemInput[],\n): ExtendedOrderItem[] => {\n const splitItems: ExtendedOrderItem[] = (items ?? []).reduce(\n (ary: ExtendedOrderItem[], item: ExtendedOrderItem) => {\n // split orderItems into multiple rows base on current selections\n let split: ExtendedOrderItem[] = (selectedItems ?? [])\n .filter(\n (itemInput) =>\n itemInput.itemId === item.itemId && itemInput.sku === item.sku,\n )\n .map((itemInput) => ({\n ...item,\n localId: itemInput.localId,\n remainingQuantity: itemInput.quantity,\n }));\n\n // max localId of current selected item, return -1 if it was unselected\n const maxLocalId: number = Math.max(\n -1,\n ...split.map((item) => item.localId ?? 0),\n );\n const selectedQty: number = split.reduce(\n (sum, item) => sum + item.remainingQuantity,\n 0,\n );\n const totalQty: number = item.remainingQuantity;\n const remainingQuantity = totalQty - selectedQty;\n\n // append another row if there is still any remaining quantity.\n if (remainingQuantity > 0) {\n split = [\n ...split,\n {\n ...item,\n localId: maxLocalId + 1, // last remaining item's localId is always max ID+1, so even if a selected item was removed, the ID won't mixup.\n remainingQuantity,\n },\n ];\n }\n\n return [...ary, ...split];\n },\n [],\n );\n\n return splitItems;\n};\n\nexport const getAdditionalItemAttrs = (\n additionalProperties?: Maybe,\n) => {\n if (!additionalProperties) {\n return null;\n }\n\n /**\n * Additional Properties come in a different format than what we need for getItemAttrs.\n * We convert these to an object of objects, formatting key, and label.\n */\n // eslint-disable-next-line\n return additionalProperties.reduce((acc: any, attr: AdditionalProperty) => {\n const { name, value } = attr;\n\n // First we split the name into an array\n const attrNameArray = name.split(\"_\");\n\n // Name attr is formatted to a label. e.g. `Rx_number` to `Rx number`\n const joinedName = attrNameArray.join(\" \");\n const label = `${joinedName.charAt(0).toUpperCase()}${joinedName.slice(1)}`;\n\n // A key is formatted to snake case. e.g. `Rx_number` to `rxNumber`\n const key =\n attrNameArray.length === 1\n ? `${name.charAt(0).toLowerCase()}${name.slice(1)}`\n : attrNameArray\n .map((word: string, i: number) => {\n let str = word.toLowerCase();\n\n if (i !== 0) {\n str = `${str.charAt(0).toUpperCase()}${str.slice(1)}`;\n }\n\n return str;\n })\n .join(\"\");\n\n acc[key] = {\n label,\n value,\n };\n\n //eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return acc;\n }, {} as AdditionalItemAttrs);\n};\n\nconst getTransformedAttribute = (\n transformType: AdditionalItemAttributeTransformTypes,\n {\n key,\n additionalItemAttrs,\n additionalItemAttributeConfig,\n currency,\n includeCurrency,\n locale,\n }: {\n key: string;\n additionalItemAttrs: AdditionalItemAttrs;\n additionalItemAttributeConfig?: Maybe;\n currency?: string;\n includeCurrency?: boolean;\n locale?: string;\n },\n): string => {\n const value = additionalItemAttrs?.[key]?.value;\n\n if (!value) {\n return \"\";\n }\n\n // If there is no transform, we return the value as is.\n if (transformType === \"none\") {\n return value;\n }\n\n // If thee transform is to concatenate, we need to find the additional attribute config for the concat key,\n // then concat it as a prefix or suffix. Usually, the concatenated attribute will be excluded from display in itemAttributeConfig.\n if (transformType === \"toConcatenate\") {\n if (!additionalItemAttributeConfig) {\n return value;\n }\n\n const thisAdditionalItemAttributeConfig =\n additionalItemAttributeConfig?.find((item) => item.attribute === key);\n const thisProperty = thisAdditionalItemAttributeConfig?.value;\n const concatenatedAttributeConfig = additionalItemAttributeConfig?.find(\n (item) => item.attribute === thisProperty?.concatKey,\n );\n const transformedConcatValue: string = getTransformedAttribute(\n concatenatedAttributeConfig?.value?.transform ?? \"none\",\n {\n key: thisProperty?.concatKey ?? \"\",\n additionalItemAttrs,\n additionalItemAttributeConfig,\n currency,\n locale,\n },\n );\n\n return thisProperty?.concatPosition === \"suffix\"\n ? `${value}${transformedConcatValue}`\n : `${transformedConcatValue}${value}`;\n }\n\n // If the transform is to currency, we use the Intl.NumberFormat to format the value as currency.\n if (transformType === \"toCurrency\") {\n return formatCurrency({\n value: parseFloat(value),\n currency,\n includeCurrency,\n locale,\n needsExponent: !value?.toString().includes(\".\"),\n });\n }\n\n // If the transform is to lowercase, we return the value in lowercase.\n if (transformType === \"toLowercase\") {\n return value.toLowerCase();\n }\n\n // Otherwise, we simply return the value as is.\n return value;\n};\n\nexport const getItemAttrs = ({\n item,\n itemDisplay,\n selectedQuantity,\n labels,\n exchangeTo,\n locale,\n includeCurrency,\n}: {\n item: ExtendedOrderItem | ExtendedReturnItem;\n itemDisplay?: Maybe;\n selectedQuantity?: number;\n labels?: {\n skuLabel?: string;\n priceLabel?: string;\n qtyLabel?: string;\n sizeLabel?: string;\n colorLabel?: string;\n };\n exchangeTo?: ExchangeVariant;\n locale?: string;\n includeCurrency?: boolean;\n}) => {\n const { additionalItemAttributeConfig, itemAttributeConfig } =\n itemDisplay || {};\n\n /**\n * In order to order and potential hide any attributes, we leverage a config array that\n * includes allowed keys in a desired order.\n *\n * itemAttributeConfig - A prop that comes from itemDisplay via useSettings() and is set in Quin retailer settings for returns.\n * In the absence of itemAttributeConfig, we use a default config DEFAULT_ITEM_ATTR_CONFIG.\n */\n const ITEM_ATTR_CONFIG = itemAttributeConfig ?? DEFAULT_ITEM_ATTR_CONFIG;\n\n const variantInfo = \"variantInfo\" in item && item.variantInfo;\n const hasVariantInfo = !!variantInfo;\n\n // We also need to set up a config array for the variant attributes as they have their own ordering logic.\n const VARIANT_ATTR_CONFIG = hasVariantInfo\n ? Object.entries(variantInfo)\n // First sort the variant entries\n .sort((a, b) => {\n const entriesLength = Object.entries(variantInfo).length;\n return (\n (a[1].optionPosition ?? entriesLength) -\n (b[1].optionPosition ?? entriesLength)\n );\n })\n // Next reduce the entries to an array of keys.\n .reduce((acc, variantEntry) => {\n acc.push(variantEntry[0]);\n return acc;\n }, [] as string[])\n : [];\n\n // We want to avoid the duplication of variant keys in the ITEM_ATTR_CONFIG.\n // Variant keys trump ITEM_ATTR_CONFIG keys, so we throw away duplicates from ITEM_ATTR_CONFIG.\n const REDUCED_ITEM_ATTR_CONFIG = ITEM_ATTR_CONFIG.reduce((acc, key) => {\n if (key && !VARIANT_ATTR_CONFIG.includes(key)) {\n acc.push(key);\n }\n return acc;\n }, [] as string[]);\n\n // Next we need to produce a final DISPLAY_ITEM_ATTR_CONFIG, spreading the VARIANT_ATTR_CONFIG into\n // the index of variantInfo in the REDUCED_ITEM_ATTR_CONFIG.\n const variantInfoIndex = REDUCED_ITEM_ATTR_CONFIG.indexOf(\"variantInfo\");\n const DISPLAY_ITEM_ATTR_CONFIG =\n variantInfoIndex >= 0\n ? [\n ...REDUCED_ITEM_ATTR_CONFIG.slice(0, variantInfoIndex),\n ...VARIANT_ATTR_CONFIG,\n ...REDUCED_ITEM_ATTR_CONFIG.slice(variantInfoIndex + 1),\n ]\n : [...REDUCED_ITEM_ATTR_CONFIG];\n\n // Finally, we begin the collection of attributes for display, by first producing slots\n // for each attribute to be placed. Empty slots will be removed at the end.\n const attributes = Array(DISPLAY_ITEM_ATTR_CONFIG.length);\n\n // QUANTITY\n const displayQuantity = selectedQuantity ? selectedQuantity : item.quantity;\n const hasQuantity = displayQuantity > 0;\n const quantityIndex = DISPLAY_ITEM_ATTR_CONFIG.indexOf(\"quantity\");\n const includeQuantity = hasQuantity && quantityIndex >= 0;\n if (includeQuantity) {\n attributes[quantityIndex] = {\n key: labels?.qtyLabel,\n value: displayQuantity,\n };\n }\n\n // VARIANT INFO\n // These booleans are used to track whether color or size are included via variant info.\n let colorIncludedInVariant = false;\n let sizeIncludedInVariant = false;\n\n // When checking whether to include the variantInfo, we need to check the REDUCED_ITEM_ATTR_CONFIG, and\n // not the DISPLAY_ITEM_ATTR_CONFIG, as the variantInfo has already been merged in\n if (hasVariantInfo && REDUCED_ITEM_ATTR_CONFIG.includes(\"variantInfo\")) {\n const variantInfoEntries = Object.entries(variantInfo);\n variantInfoEntries.forEach(([key, value]) => {\n const index = DISPLAY_ITEM_ATTR_CONFIG.indexOf(key);\n if (\n !exchangeTo?.exchangeForCredit &&\n exchangeTo?.variantInfo?.[key] &&\n exchangeTo?.variantInfo?.[key].value !== value.value\n ) {\n attributes[index] = {\n key: `${value.optionName ?? value.optionId}: `,\n transaction: {\n before: value.name ?? value.value,\n after:\n exchangeTo?.variantInfo?.[key]?.name ??\n exchangeTo?.variantInfo?.[key]?.value,\n },\n };\n } else {\n attributes[index] = {\n key: `${value.optionName ?? value.optionId}: `,\n value: value.name ?? value.value,\n };\n }\n\n // If color or size are included in the variant info, we track them in the provided booleans.\n if (key === \"color\") colorIncludedInVariant = true;\n if (key === \"size\") sizeIncludedInVariant = true;\n });\n }\n\n // COLOR IF NOT INCLUDED IN VARIANT INFO\n const colorIndex = DISPLAY_ITEM_ATTR_CONFIG.indexOf(\"color\");\n if (!colorIncludedInVariant && item.color && colorIndex >= 0) {\n attributes[colorIndex] = {\n key: labels?.colorLabel,\n value: item.color,\n };\n }\n\n // SIZE IF NOT INCLUDED IN VARIANT INFO\n const sizeIndex = DISPLAY_ITEM_ATTR_CONFIG.indexOf(\"size\");\n if (!sizeIncludedInVariant && item.size && sizeIndex >= 0) {\n attributes[sizeIndex] = {\n key: labels?.sizeLabel,\n value: item.size,\n };\n }\n\n // SKU\n const sku = exchangeTo?.sku ?? item.sku;\n const skuIndex = DISPLAY_ITEM_ATTR_CONFIG.indexOf(\"sku\");\n if (sku && skuIndex >= 0) {\n attributes[skuIndex] = {\n key: labels?.skuLabel,\n value: sku,\n };\n }\n\n // PRICE\n const priceIndex = DISPLAY_ITEM_ATTR_CONFIG.indexOf(\"price\");\n if (priceIndex >= 0) {\n if (typeof item.price?.value === \"number\") {\n let priceItem;\n if (\n !exchangeTo?.exchangeForCredit &&\n typeof exchangeTo?.price.value === \"number\" &&\n exchangeTo.price.value !== item.price.value\n ) {\n priceItem = {\n key: labels?.priceLabel,\n transaction: {\n before: (\n \n ),\n after: (\n \n ),\n },\n };\n } else if (\n \"discountedPrice\" in item &&\n typeof item.discountedPrice?.value === \"number\" &&\n item.price.value !== item.discountedPrice.value\n ) {\n priceItem = {\n key: labels?.priceLabel,\n transaction: {\n before: (\n \n ),\n after: (\n \n ),\n },\n };\n } else {\n priceItem = {\n key: labels?.priceLabel,\n value: (\n \n ),\n };\n }\n\n attributes[priceIndex] = priceItem;\n }\n }\n\n // ADDITIONAL ATTRIBUTES\n // additionalProperties are passed in via the item and need display.\n // NOTE: They will only be displayed if configured via itemDisplay.itemAttributeConfig in Quin.\n const additionalItemAttrs = getAdditionalItemAttrs(item.additionalProperties);\n\n if (additionalItemAttrs) {\n Object.keys(additionalItemAttrs).forEach((key) => {\n const index = DISPLAY_ITEM_ATTR_CONFIG.indexOf(key);\n if (index >= 0) {\n // To find the current additional attribute, we need to find the object with the attribute key that matches the current key.\n const thisAdditionalItemAttributeConfig =\n additionalItemAttributeConfig?.find((item) => item.attribute === key);\n\n // If there is a custom label in the this attribute config, we use it, otherwise we use the label from the additionalItemAttrs object which comes from the retailer.\n const label =\n thisAdditionalItemAttributeConfig?.value?.label ??\n additionalItemAttrs[key].label;\n\n // Then we create a key value pair for rendering the attribute. If configured, we transform the value using getTransformedAttribute.\n attributes[index] = {\n key: `${label}: `, // We add colon and space to the label.\n value: getTransformedAttribute(\n thisAdditionalItemAttributeConfig?.value?.transform ?? \"none\",\n {\n key,\n additionalItemAttrs,\n additionalItemAttributeConfig,\n includeCurrency,\n locale,\n },\n ),\n };\n }\n });\n }\n\n // Return the filtered attributes to remove any empty slots in the original attributes array.\n // Empty slots can occur if a given attributes doesn't pass a give check. e.g. quantity is 0\n return attributes.filter((n) => n != null);\n};\n\nexport const getItemProps = ({\n item,\n exchangeTo,\n}: {\n item: ExtendedOrderItem;\n exchangeTo?: ExchangeVariant;\n showTag?: boolean;\n}) => {\n const returnType: ReturnReasonsReturnType =\n exchangeTo && exchangeTo.exchangeForCredit\n ? \"exchangeForCredit\"\n : exchangeTo\n ? \"exchange\"\n : \"return\";\n const title =\n returnType === \"return\"\n ? item.name\n : (exchangeTo?.name ??\n exchangeTo?.variantInfo?.product?.name ??\n item.name);\n const thumbnail =\n returnType === \"exchange\"\n ? { old: item.imageUrl, new: exchangeTo?.imageUrl }\n : item.imageUrl;\n\n return {\n thumbnail,\n title,\n returnType,\n };\n};\n","import React from \"react\";\nimport { useTheme } from \"@material-ui/core/styles\";\nimport IconButton from \"@material-ui/core/IconButton\";\nimport RedoIcon from \"@material-ui/icons/Redo\";\nimport LoopIcon from \"@material-ui/icons/Loop\";\nimport Image from \"material-ui-image\";\n\nimport { makeRootStyles } from \"../../../theme/styles\";\nimport ItemThumbnail from \"../Item/ItemThumbnail\";\nimport StoreCredit from \"../../../../shared/components/icons/StoreCredit\";\n\nexport const useStyles = makeRootStyles(\n (theme) => ({\n root: {\n position: \"relative\",\n },\n prevImageContainer: {\n position: \"absolute\",\n left: 0,\n top: 0,\n zIndex: 1,\n transform: `translate(-${theme.spacing(1)}px, -${theme.spacing(1)}px)`,\n },\n prevImageUsingIcon: {\n width: theme.spacing(8),\n height: theme.spacing(8),\n backgroundColor: theme.palette.common.white,\n },\n icon: {\n pointerEvents: \"none\",\n position: \"absolute\",\n top: 0,\n right: 0,\n transform: `translate(50%, 50%)`,\n backgroundColor: theme.palette.common.white,\n },\n }),\n { name: \"N0ExchangeItemThumbnail\" },\n);\n\nfunction ExchangeItemThumbnail({\n className,\n imageAltTxt,\n imageStyle,\n imageUrl,\n prevImageAltTxt,\n prevImageUrl,\n storeCredit,\n size = \"medium\",\n}) {\n const classes = useStyles();\n const theme = useTheme();\n const sizeMapping = {\n small: theme.spacing(6),\n medium: theme.spacing(8),\n large: theme.spacing(10),\n };\n const width = sizeMapping[size];\n const height = sizeMapping[size];\n\n return (\n