UNPKG

@commercetools-frontend/react-notifications

Version:
826 lines (797 loc) • 45.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _pt = require('prop-types'); var react = require('react'); var jsxRuntime = require('@emotion/react/jsx-runtime'); var _Object$keys = require('@babel/runtime-corejs3/core-js-stable/object/keys'); var _Object$getOwnPropertySymbols = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols'); var _filterInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/filter'); var _Object$getOwnPropertyDescriptor = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor'); var _forEachInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/for-each'); var _Object$getOwnPropertyDescriptors = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors'); var _Object$defineProperties = require('@babel/runtime-corejs3/core-js-stable/object/define-properties'); var _Object$defineProperty = require('@babel/runtime-corejs3/core-js-stable/object/define-property'); var _defineProperty = require('@babel/runtime-corejs3/helpers/defineProperty'); var _objectWithoutProperties = require('@babel/runtime-corejs3/helpers/objectWithoutProperties'); var reactIntl = require('react-intl'); var constants = require('@commercetools-frontend/constants'); var hooks = require('@commercetools-uikit/hooks'); var icons = require('@commercetools-uikit/icons'); var SecondaryIconButton = require('@commercetools-uikit/secondary-icon-button'); var utils = require('@commercetools-uikit/utils'); var _startsWithInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/starts-with'); var omitBy = require('lodash/omitBy'); var react$1 = require('@emotion/react'); var designSystem = require('@commercetools-uikit/design-system'); var _mapInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/map'); var _valuesInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/values'); var reactRedux = require('react-redux'); var notifications = require('@commercetools-frontend/notifications'); var sentry = require('@commercetools-frontend/sentry'); var _includesInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/includes'); var has = require('lodash/has'); var Spacings = require('@commercetools-uikit/spacings'); var _sliceInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/slice'); var reselect = require('reselect'); var isNumber = require('lodash/isNumber'); var actionsGlobal = require('@commercetools-frontend/actions-global'); function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; } var _pt__default = /*#__PURE__*/_interopDefault(_pt); var _Object$keys__default = /*#__PURE__*/_interopDefault(_Object$keys); var _Object$getOwnPropertySymbols__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertySymbols); var _filterInstanceProperty__default = /*#__PURE__*/_interopDefault(_filterInstanceProperty); var _Object$getOwnPropertyDescriptor__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertyDescriptor); var _forEachInstanceProperty__default = /*#__PURE__*/_interopDefault(_forEachInstanceProperty); var _Object$getOwnPropertyDescriptors__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertyDescriptors); var _Object$defineProperties__default = /*#__PURE__*/_interopDefault(_Object$defineProperties); var _Object$defineProperty__default = /*#__PURE__*/_interopDefault(_Object$defineProperty); var SecondaryIconButton__default = /*#__PURE__*/_interopDefault(SecondaryIconButton); var _startsWithInstanceProperty__default = /*#__PURE__*/_interopDefault(_startsWithInstanceProperty); var omitBy__default = /*#__PURE__*/_interopDefault(omitBy); var _mapInstanceProperty__default = /*#__PURE__*/_interopDefault(_mapInstanceProperty); var _valuesInstanceProperty__default = /*#__PURE__*/_interopDefault(_valuesInstanceProperty); var _includesInstanceProperty__default = /*#__PURE__*/_interopDefault(_includesInstanceProperty); var has__default = /*#__PURE__*/_interopDefault(has); var Spacings__default = /*#__PURE__*/_interopDefault(Spacings); var _sliceInstanceProperty__default = /*#__PURE__*/_interopDefault(_sliceInstanceProperty); var isNumber__default = /*#__PURE__*/_interopDefault(isNumber); // NOTE: This string will be replaced on build time with the package version. var version = "23.2.2"; const Context = /*#__PURE__*/react.createContext(() => null); function NotificationProviderForCustomComponent(props) { return jsxRuntime.jsx(Context.Provider, { value: props.mapNotificationToComponent, children: props.children }); } NotificationProviderForCustomComponent.propTypes = {}; NotificationProviderForCustomComponent.displayName = 'NotificationProviderForCustomComponent'; const useCustomNotificationComponent = () => react.useContext(Context); function filterDataAttributes(obj) { return omitBy__default["default"](obj, (_value, key) => !_startsWithInstanceProperty__default["default"](key).call(key, 'data-')); } var messages = reactIntl.defineMessages({ hideNotification: { id: 'Notification.hideNotification', description: 'Label for button to hide notification', defaultMessage: 'Hide notification' } }); const getColorByType = value => { switch (value) { case constants.NOTIFICATION_KINDS_SIDE.success: return designSystem.designTokens.colorSuccess; case constants.NOTIFICATION_KINDS_SIDE.info: return designSystem.designTokens.colorInfo; case constants.NOTIFICATION_KINDS_SIDE.error: return designSystem.designTokens.colorError; case constants.NOTIFICATION_KINDS_SIDE.warning: return designSystem.designTokens.colorWarning60; default: return 'transparent'; } }; const getBorderColor = notificationKind => { switch (notificationKind) { case constants.NOTIFICATION_KINDS_SIDE.success: return designSystem.designTokens.colorSuccess85; case constants.NOTIFICATION_KINDS_SIDE.info: return designSystem.designTokens.colorInfo85; case constants.NOTIFICATION_KINDS_SIDE.error: return designSystem.designTokens.colorError85; case constants.NOTIFICATION_KINDS_SIDE.warning: return designSystem.designTokens.colorWarning85; default: return 'transparent'; } }; const showNotificationAnimation = react$1.keyframes` 0% { max-height: 0; padding-top: 0; padding-bottom: 0; overflow: hidden; } 100% { max-height: 200px; } `; const showNotificationSideAnimation = react$1.keyframes` 0% { transform: translateX(50px); } 100% { transform: translateX(0); } `; const getStylesForNotificationIcon = props => /*#__PURE__*/react$1.css("display:flex;align-items:center;justify-content:center;position:absolute;left:0;top:0;width:48px;height:100%;color:", designSystem.designTokens.colorSurface, ";border-radius:3px 0 0 3px;background:", getColorByType(props.type), ";" + ("" ), "" ); const getStylesForCloseIcon = props => /*#__PURE__*/react$1.css("display:flex;justify-content:center;& svg{width:16px;height:16px;}", props.domain !== constants.NOTIFICATION_DOMAINS.SIDE ? '& svg { fill: ' + designSystem.designTokens.colorSurface + '; }' : '', ";" + ("" ), "" ); const getStylesForContent = props => { const fontColor = props.domain === constants.NOTIFICATION_DOMAINS.SIDE ? designSystem.designTokens.colorSolid : designSystem.designTokens.colorSurface; return /*#__PURE__*/react$1.css("flex-basis:100%;flex-grow:1;padding:", `0 ${designSystem.designTokens.spacingM}`, ";margin:0;font-size:", props.domain === constants.NOTIFICATION_DOMAINS.SIDE ? '1rem' : 'inherit', ";color:", fontColor, ";p{color:", fontColor, ";}ul{padding:0;margin:0;list-style:none;}" + ("" ), "" ); }; const getStylesForNotification = props => { const baseStyles = /*#__PURE__*/react$1.css("position:relative;display:flex;align-items:center;padding:", designSystem.designTokens.spacingM, ";color:", designSystem.designTokens.colorSurface, ";" + ("" ), "" ); const pageStyles = /*#__PURE__*/react$1.css(baseStyles, ";animation:", showNotificationAnimation, " 0.3s forwards;text-align:center;background-color:", props.fixed ? 'transparent' : getColorByType(props.type), ";>*+*{margin-left:", designSystem.designTokens.spacingS, ";}" + ("" ), "" ); switch (props.domain) { case constants.NOTIFICATION_DOMAINS.GLOBAL: return /*#__PURE__*/react$1.css(pageStyles, ";background-color:", getColorByType(props.type), ";" + ("" ), "" ); case constants.NOTIFICATION_DOMAINS.PAGE: return pageStyles; case constants.NOTIFICATION_DOMAINS.SIDE: { const sideStyles = /*#__PURE__*/react$1.css(baseStyles, ";animation:", showNotificationAnimation, " 0.3s forwards;padding:", designSystem.designTokens.spacingM, " ", designSystem.designTokens.spacingM, " ", designSystem.designTokens.spacingM, " 50px!important;text-align:left;background:", designSystem.designTokens.colorSurface, ";border:1px solid ", getBorderColor(props.type), ";box-shadow:0px 2px 5px 0px rgba(0, 0, 0, 0.15);border-radius:", designSystem.designTokens.borderRadius6, ";word-break:break-word;hyphens:auto;" + ("" ), "" ); if (props.fixed) return sideStyles; return /*#__PURE__*/react$1.css(sideStyles, ";animation:", showNotificationSideAnimation, " 0.3s forwards;position:relative;z-index:10000;margin-top:", designSystem.designTokens.spacingL, "!important;right:", designSystem.designTokens.spacingXl, ";float:right;clear:both;max-width:50%;" + ("" ), "" ); } default: return /*#__PURE__*/react$1.css("" , "" ); } }; const _excluded$2 = ["fixed"]; function ownKeys$4(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$4(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context = ownKeys$4(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context2 = ownKeys$4(Object(t))).call(_context2, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; } const sequentialId = utils.createSequentialId('notification-'); const NotificationIcon = props => { if (props.type === constants.NOTIFICATION_KINDS_SIDE.error) { return jsxRuntime.jsx(icons.ErrorIcon, { color: props.color }); } if (props.type === constants.NOTIFICATION_KINDS_SIDE.info) { return jsxRuntime.jsx(icons.InformationIcon, { color: props.color }); } if (props.type === constants.NOTIFICATION_KINDS_SIDE.warning) { return jsxRuntime.jsx(icons.WarningIcon, { color: props.color }); } return jsxRuntime.jsx(icons.CheckBoldIcon, { color: props.color }); }; NotificationIcon.propTypes = {}; NotificationIcon.displayName = 'NotificationIcon'; const Notification = _ref => { let _ref$fixed = _ref.fixed, fixed = _ref$fixed === void 0 ? false : _ref$fixed, props = _objectWithoutProperties(_ref, _excluded$2); const intl = reactIntl.useIntl(); const id = hooks.useFieldId(undefined, sequentialId); return jsxRuntime.jsxs("div", _objectSpread$4(_objectSpread$4({ role: "alertdialog", "aria-describedby": id, css: getStylesForNotification(_objectSpread$4({ fixed }, props)) }, filterDataAttributes(props)), {}, { children: [jsxRuntime.jsx("div", { id: id, css: getStylesForContent(_objectSpread$4({ fixed }, props)), children: props.children }), props.onCloseClick ? jsxRuntime.jsx("div", { css: getStylesForCloseIcon(_objectSpread$4({ fixed }, props)), children: jsxRuntime.jsx(SecondaryIconButton__default["default"], { label: intl.formatMessage(messages.hideNotification), onClick: props.onCloseClick, icon: jsxRuntime.jsx(icons.CloseBoldIcon, {}), size: "30" }) }) : null, props.domain === constants.NOTIFICATION_DOMAINS.SIDE ? jsxRuntime.jsx("div", { css: getStylesForNotificationIcon(_objectSpread$4({ fixed }, props)), children: jsxRuntime.jsx(NotificationIcon, { type: props.type, color: "surface" }) }) : null] })); }; Notification.propTypes = {}; Notification.displayName = 'Notification'; var apiErrorMessages = reactIntl.defineMessages({ General: { id: 'ApiError.General', description: 'A general error message, usually because of internal application problems. The user should not know the details of the error.', defaultMessage: 'Sorry, but there seems to be something wrong. Please try again. If you are seeing this message for the second time, please contact our support team.' }, // Custom API Error messages (without a matching error.code) OverlappingPrices: { id: 'ApiError.OverlappingPrices', description: '', defaultMessage: 'Sorry, but a price with these details already exists. Please amend the price details so that they do not overlap with another price before saving.' }, ConcurrentModificationBulkEdit: { id: 'ApiError.ConcurrentModificationBulkEdit', description: 'User does a bulk update but someone else has saved changes for that element', defaultMessage: 'Sorry, but we were unable to save your changes as someone else made changes to this same source while you were editing.' }, // API errors ConcurrentModification: { id: 'ApiError.ConcurrentModification', description: 'User edits form and clicks Save but someone else has saved changes for this element while they were editing', defaultMessage: 'Sorry, but we were unable to save your changes as someone else made changes to this same source while you were editing. Please refresh the page and re-enter your changes.' }, DuplicateAttributeValue: { id: 'ApiError.DuplicateAttributeValue', description: 'User tries to enter the same attribute value for an attribute with the Unique constraint', defaultMessage: 'This value has already been used for another variant. The "{name}" value must be unique for all variants for this product. Please enter a different value.' }, DuplicateAttributeValues: { id: 'ApiError.DuplicateAttributeValues', description: 'User tries to enter existing attribute values for a combination of attributes with the CombinationUnique constraint', defaultMessage: 'This combination has already been used for another variant. The combination of these attributes must be unique across all variants. Please enter a different version.' }, DuplicateField: { id: 'ApiError.DuplicateField', description: 'The given field must be unique across the project', defaultMessage: 'The value for the field "{field}" has already been used. Please choose another value for this field.' }, DuplicateSlug: { id: 'ApiError.DuplicateSlug', description: 'User tries to create a resource with an already existing slug', defaultMessage: '"{slugValue}" is already in use. Please enter a new slug value for this product.' }, DuplicatePriceScope: { id: 'ApiError.DuplicatePriceScope', description: 'User tries to create a price with the exact same values as for an already existing price', defaultMessage: 'A price with the same scope already exists for this product variant. Make sure that the combination of currency, country, customer group, channel and valid dates is unique per price.' }, DuplicateStandalonePriceScope: { id: 'ApiError.DuplicateStandalonePriceScope', description: 'User tries to create a standalone price with the exact same values as for an already existing standalone price', defaultMessage: 'A price with the same scope already exists for this product variant. The combination of currency, country, customer group, channel and validity date must be unique for each price per SKU.' }, DuplicateVariantValues: { id: 'ApiError.DuplicateVariantValues', description: 'User tries to generate a variant with the same SKU or attribute values', defaultMessage: 'The same variant already exists for this product. Please enter different values for the fields.' }, InvalidDateRange: { id: 'ApiError.InvalidDateRange', description: 'User tries to input an invalid date range', defaultMessage: 'The value entered for the field {field} is invalid. The start date must be before the end date' }, InvalidField: { id: 'ApiError.InvalidField', description: 'User enters an invalid value for a field.', defaultMessage: 'The value entered is not valid for the field "{field}".' }, InvalidSlug: { id: 'ApiError.InvalidSlug', description: 'User enters an invalid value for the product slug', defaultMessage: 'Slugs may only contain alphanumeric (0-9A-Z) characters, underscores and hyphens and must have a length between 2 and 256 characters.' }, OverlappingPriceValidity: { id: 'ApiError.OverlappingPriceValidity', description: 'Returned when a given price validity period conflicts with an existing one', defaultMessage: 'Another price with overlapping validity dates exists. Each price combination needs to have unique validity dates.' }, OverlappingStandalonePriceValidity: { id: 'ApiError.OverlappingStandalonePriceValidity', description: 'Returned when a given standalone price validity period conflicts with an existing one', defaultMessage: 'Another price with overlapping validity dates exists. Each price combination needs to have unique validity dates.' }, PendingOperation: { id: 'ApiError.PendingOperation', description: 'User tries to start a new process when one is already underway', defaultMessage: 'Sorry, but we are still processing the previous request. Please try again once it is complete.' }, ResourceNotFound: { id: 'ApiError.ResourceNotFound', description: 'System cannot find the functionality or screen that the user is trying to access.', defaultMessage: 'Sorry, but we cannot find what you are looking for.' }, ReferenceExists: { id: 'ApiError.ReferenceExists', description: 'User tries to delete an element that has an existing reference to it from another element', defaultMessage: 'Can not delete a source while it is referenced from at least one "{referencedBy}".' }, RequiredField: { id: 'ApiError.RequiredField', description: 'User does not enter a required field', defaultMessage: '"{field}" is a required field. Please enter a value.' }, RequiredFields: { // Client side validation id: 'ApiError.RequiredFields', description: 'User submits a form without having completed all mandatory fields', defaultMessage: 'Please enter values for the following required fields: {fields}' }, SemanticError: { id: 'ApiError.SemanticError', description: 'User enters a predicate query that throws a system semantic error', defaultMessage: 'Semantic error: the given Predicate is not valid. Please read the documentation to define a correct predicate.' }, SyntaxError: { id: 'ApiError.SyntaxError', description: 'User enters a predicate query that throws a system syntax error', defaultMessage: 'Syntax error: the given Predicate is not valid. Please read the documentation to define a correct predicate.' }, Unauthorized: { id: 'ApiError.Unauthorized', description: 'The access token is not valid anymore, or the user does not have a valid one', defaultMessage: 'Sorry, but you are not authorized to access this feature.' }, Forbidden: { id: 'ApiError.Forbidden', description: 'User tries to access a view that they do not have permission for', defaultMessage: 'You are not authorized to access this feature. Please contact your system administrator with any further questions.' }, ExtensionNoResponse: { id: 'ApiError.ExtensionNoResponse', description: 'User tries to access a view that they do not have permission for', defaultMessage: 'Sorry, we could not perform the requested action due to an API extension not responding.' }, ExtensionBadResponse: { id: 'ApiError.ExtensionBadResponse', description: 'User tries to access a view that they do not have permission for', defaultMessage: 'Sorry, we could not perform the requested action due to failed processing of an API extension response.' }, ExtensionUpdateActionsFailed: { id: 'ApiError.ExtensionUpdateActionsFailed', description: 'User tries to access a view that they do not have permission for', defaultMessage: 'Sorry, we could not perform the requested action. It is not possible to perform the update actions as instructed by the API extension.' }, TaxCategoryDuplicateCountry: { id: 'ApiError.TaxCategoryDuplicateCountry', description: 'User inputs duplicate country and/or state into tax category form', defaultMessage: 'Duplicate tax rates submitted. Please remove the duplicates.' }, MaxResourceLimitExceeded: { id: 'ApiError.MaxResourceLimitExceeded', description: 'User attempts to create a resource while having already reached the limit', defaultMessage: 'The project reached the limit for the resource. To add more resources delete existing ones or reach out to the administrator or contact customer support.' } }); function ownKeys$3(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$3(e) { for (var r = 1; r < arguments.length; r++) { var _context6, _context7; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context6 = ownKeys$3(Object(t), !0)).call(_context6, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context7 = ownKeys$3(Object(t))).call(_context7, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; } const regexInvalidOperationRequiredAttribute = /^Required attribute '(.*)' cannot be removed/; // `error` fields for REST errors // `error.extensions` fields for GraphQL errors // The values passed to the Intl message must be a map of scalar values. // Some of the message values are already mapped in the `getSpecialFormattedMessageByErrorCode` // function. Here we map other possible error properties that can be // used in the message. const mapErrorFieldsToMessageValues = error => { const errorField = error.extensions?.field || error.field; if (errorField) { return { field: errorField }; } const errorReferencedBy = error.extensions?.referencedBy || error.referencedBy; if (errorReferencedBy) { return { referencedBy: errorReferencedBy }; } return {}; }; // Type-guard validation for error code to be included in the message object keys. const hasErrorCodeAMatchingMessage = errorCode => errorCode in apiErrorMessages; const FormattedErrorMessage = props => { var _context2; const intl = reactIntl.useIntl(); // Attempt to map the error by code const extensionErrorCode = props.error.extensions?.code ?? props.error.code; const messageCode = extensionErrorCode && hasErrorCodeAMatchingMessage(extensionErrorCode) ? apiErrorMessages[extensionErrorCode] : undefined; react.useEffect(() => { if (!messageCode) { var _context; // This error is not mapped / translated yet, // we log, report it to sentry and show the original error, unless `error.code` is `invalid_scope` // which an error code emitted for expired project(s) if (extensionErrorCode !== 'invalid_scope' && !_includesInstanceProperty__default["default"](_context = props.error.message).call(_context, 'has expired')) { sentry.reportErrorToSentry(new Error('Unmapped error'), { extra: props.error }); } } }, [extensionErrorCode, messageCode, props.error]); if (messageCode) { // The `error` object might contain extra fields for the specific `code`. return jsxRuntime.jsx(jsxRuntime.Fragment, { children: intl.formatMessage(messageCode, mapErrorFieldsToMessageValues(props.error)) }); } return jsxRuntime.jsx(jsxRuntime.Fragment, { children: _filterInstanceProperty__default["default"](_context2 = [props.error.message, props.error.detailedErrorMessage && `(${props.error.detailedErrorMessage})`]).call(_context2, Boolean).join(' ') }); }; FormattedErrorMessage.propTypes = { error: _pt__default["default"].any.isRequired }; FormattedErrorMessage.displayName = 'FormattedErrorMessage'; const ApiErrorMessage = props => { const intl = reactIntl.useIntl(); // Attempt to map the error to a specific error message const specialFormattedMessage = getSpecialFormattedMessageByErrorCode(props.error, intl); if (specialFormattedMessage) { return jsxRuntime.jsx(jsxRuntime.Fragment, { children: specialFormattedMessage }); } return jsxRuntime.jsx(FormattedErrorMessage, _objectSpread$3({}, props)); }; ApiErrorMessage.propTypes = {}; ApiErrorMessage.displayName = 'ApiErrorMessage'; function getSpecialFormattedMessageByErrorCode(error, intl) { var _context3, _context4, _context5; const extensionErrorCode = error.extensions?.code ?? error.code; if (error.errorByExtension) { let extensionMessage; if (error.localizedMessage) { extensionMessage = error.localizedMessage[intl.locale]; } return extensionMessage || error.message; } if (!extensionErrorCode || extensionErrorCode === 'InvalidInput') return intl.formatMessage(apiErrorMessages.General); // TODO: this is a temporary solution until we have proper pages about 403 if (extensionErrorCode === 'insufficient_scope') return intl.formatMessage(apiErrorMessages.Forbidden); if (extensionErrorCode === 'DuplicateField') { const errorField = error.extensions?.field || error.field; const errorDuplicateValue = error.extensions?.duplicateValue || error.duplicateValue; if (errorField === 'slug') { return intl.formatMessage(apiErrorMessages.DuplicateSlug, { slugValue: errorDuplicateValue }); } else { return intl.formatMessage(apiErrorMessages.DuplicateField, { field: errorField }); } } // Try to match the error with a custom error message if (has__default["default"](error, 'invalidValue') && has__default["default"](error.invalidValue, 'overlappingPrices')) return intl.formatMessage(apiErrorMessages.OverlappingPrices); if (extensionErrorCode === 'InvalidOperation' && _includesInstanceProperty__default["default"](_context3 = error.message).call(_context3, 'validFrom') && _includesInstanceProperty__default["default"](_context4 = error.message).call(_context4, 'validUntil')) { return intl.formatMessage(apiErrorMessages.InvalidDateRange, { field: 'validFrom' }); } if (extensionErrorCode === 'InvalidOperation' && _includesInstanceProperty__default["default"](_context5 = error.message).call(_context5, 'Duplicate tax rate for')) { return intl.formatMessage(apiErrorMessages.TaxCategoryDuplicateCountry); } if (extensionErrorCode === 'InvalidOperation' && regexInvalidOperationRequiredAttribute.test(error.message)) { const attrName = error.message.replace(regexInvalidOperationRequiredAttribute, '$1'); return intl.formatMessage(apiErrorMessages.RequiredField, { field: attrName }); } // TODO: A concern has be raised that we can't accurately distinguish // this error (invalid start / end dates with prices) from other price // errors. We should investigate this further. if (extensionErrorCode === 'InvalidField' && error.field === 'price' && has__default["default"](error, 'invalidValue') && has__default["default"](error.invalidValue, 'validFrom') && has__default["default"](error.invalidValue, 'validUntil')) return intl.formatMessage(apiErrorMessages.InvalidDateRange, { field: error.field }); if (extensionErrorCode === 'DuplicateAttributeValue' && error.attribute) { return intl.formatMessage(apiErrorMessages.DuplicateAttributeValue, { name: error.attribute.name }); } if (extensionErrorCode === 'MaxResourceLimitExceeded') { return intl.formatMessage(apiErrorMessages.MaxResourceLimitExceeded); } return; } const ApiErrorNotification = props => { var _context; return jsxRuntime.jsx(Notification, { type: "error", domain: props.notification.domain, onCloseClick: props.dismiss, children: jsxRuntime.jsx("ul", { children: _valuesInstanceProperty__default["default"](props.notification) && _mapInstanceProperty__default["default"](_context = _valuesInstanceProperty__default["default"](props.notification).errors).call(_context, (error, idx) => { const extensionErrorCode = error.extensions?.code ?? error.code; const shouldLogErrorToConsole = !extensionErrorCode && "production" === 'development'; if (shouldLogErrorToConsole) { /** * NOTE: This is an API error which usually contains * a `code` property such as `DuplicateField` or `InvalidOperation`. * If this `code` does not exist the API is not conforming to its * own error specification. */ // eslint-disable-next-line no-console console.error('Unknown API error', error); } return jsxRuntime.jsx("li", { children: jsxRuntime.jsx(ApiErrorMessage, { error: error }) }, idx); }) }) }); }; ApiErrorNotification.propTypes = {}; ApiErrorNotification.displayName = 'ApiErrorNotification'; const GenericNotification = props => jsxRuntime.jsx(Notification, { domain: props.notification.domain, type: props.notification.kind, onCloseClick: props.dismiss, children: props.notification.text }); GenericNotification.propTypes = {}; GenericNotification.displayName = 'GenericNotification'; function ownKeys$2(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$2(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context = ownKeys$2(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context2 = ownKeys$2(Object(t))).call(_context2, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; } const UnexpectedErrorNotification = props => jsxRuntime.jsx(Notification, { type: "error", domain: props.notification.domain, onCloseClick: props.dismiss, children: jsxRuntime.jsxs(Spacings__default["default"].Stack, { children: [jsxRuntime.jsx("div", { children: jsxRuntime.jsx(reactIntl.FormattedMessage, _objectSpread$2({}, apiErrorMessages.General)) }), _valuesInstanceProperty__default["default"](props.notification) && _valuesInstanceProperty__default["default"](props.notification).errorId && jsxRuntime.jsx("div", { children: `ID (${_valuesInstanceProperty__default["default"](props.notification).errorId})` })] }) }); UnexpectedErrorNotification.propTypes = {}; UnexpectedErrorNotification.displayName = 'UnexpectedErrorNotification'; const getStyles = props => { const baseStyles = /*#__PURE__*/react$1.css("color:", designSystem.customProperties.colorSurface, ";position:relative;width:100%;z-index:19999;" + ("" ), "" ); switch (props.domain) { case constants.NOTIFICATION_DOMAINS.GLOBAL: return /*#__PURE__*/react$1.css(baseStyles, ";text-align:center;width:100%!important;" + ("" ), "" ); case constants.NOTIFICATION_DOMAINS.PAGE: return /*#__PURE__*/react$1.css(baseStyles, ";" + ("" ), "" ); case constants.NOTIFICATION_DOMAINS.SIDE: return /*#__PURE__*/react$1.css(baseStyles, ";position:absolute;text-align:left;height:0;overflow:visible;" + ("" ), "" ); default: return /*#__PURE__*/react$1.css("" , "" ); } }; // These selectors are okay memoization-wise, but once a single notifications // is added or removed the memoization for all domain selectors is reset const selectNotifications = state => state.notifications; const selectGlobalNotifications = reselect.createSelector(selectNotifications, notifications => { var _context; return _sliceInstanceProperty__default["default"](_context = _filterInstanceProperty__default["default"](notifications).call(notifications, notification => notification.domain === constants.NOTIFICATION_DOMAINS.GLOBAL) // Return only 1 at a time ).call(_context, 0, 1); }); const selectPageNotifications = reselect.createSelector(selectNotifications, notifications => _filterInstanceProperty__default["default"](notifications).call(notifications, notification => notification.domain === constants.NOTIFICATION_DOMAINS.PAGE)); const selectSideNotifications = reselect.createSelector(selectNotifications, notifications => _filterInstanceProperty__default["default"](notifications).call(notifications, notification => notification.domain === constants.NOTIFICATION_DOMAINS.SIDE)); const _excluded$1 = ["values"], _excluded2 = ["text"], _excluded3 = ["values"], _excluded4 = ["text"], _excluded5 = ["text"]; function ownKeys$1(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$1(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context = ownKeys$1(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context2 = ownKeys$1(Object(t))).call(_context2, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; } const NotificationsListGlobal = props => { const dispatch = reactRedux.useDispatch(); const mapCustomNotificationToComponent = useCustomNotificationComponent(); const notifications$1 = reactRedux.useSelector(selectGlobalNotifications); return jsxRuntime.jsx("div", { id: `notifications-${props.domain}`, css: getStyles(props), children: _mapInstanceProperty__default["default"](notifications$1).call(notifications$1, notification => { // 1. Check if there is a custom notification component first const CustomNotificationComponent = mapCustomNotificationToComponent(notification); if (CustomNotificationComponent) { return jsxRuntime.jsx(CustomNotificationComponent, { notification: notification, dismiss: () => { dispatch(notifications.removeNotification(notification.id)); } }, notification.id); } switch (notification.kind) { case constants.NOTIFICATION_KINDS_GLOBAL.error: case constants.NOTIFICATION_KINDS_GLOBAL.warning: case constants.NOTIFICATION_KINDS_GLOBAL.info: case constants.NOTIFICATION_KINDS_GLOBAL.success: { _valuesInstanceProperty__default["default"](notification); const genericNotification = _objectWithoutProperties(notification, _excluded$1); return jsxRuntime.jsx(GenericNotification, { notification: genericNotification, dismiss: () => { dispatch(notifications.removeNotification(notification.id)); } }, notification.id); } case constants.NOTIFICATION_KINDS_GLOBAL['unexpected-error']: { notification.text; const errorNotification = _objectWithoutProperties(notification, _excluded2); return jsxRuntime.jsx(UnexpectedErrorNotification, { notification: errorNotification, dismiss: () => { dispatch(notifications.removeNotification(notification.id)); } }, notification.id); } default: { return null; } } }) }); }; const NotificationsListPage = props => { const dispatch = reactRedux.useDispatch(); const mapCustomNotificationToComponent = useCustomNotificationComponent(); const notifications$1 = reactRedux.useSelector(selectPageNotifications); return jsxRuntime.jsx("div", { id: `notifications-${props.domain}`, css: getStyles(props), children: _mapInstanceProperty__default["default"](notifications$1).call(notifications$1, notification => { // 1. Check if there is a custom notification component first const CustomNotificationComponent = mapCustomNotificationToComponent(notification); if (CustomNotificationComponent) { return jsxRuntime.jsx(CustomNotificationComponent, { notification: notification, dismiss: () => { dispatch(notifications.removeNotification(notification.id)); } }, notification.id); } switch (notification.kind) { case constants.NOTIFICATION_KINDS_PAGE.error: case constants.NOTIFICATION_KINDS_PAGE.warning: case constants.NOTIFICATION_KINDS_PAGE.info: case constants.NOTIFICATION_KINDS_PAGE.success: { _valuesInstanceProperty__default["default"](notification); const genericNotification = _objectWithoutProperties(notification, _excluded3); return jsxRuntime.jsx(GenericNotification, { notification: _objectSpread$1(_objectSpread$1({}, genericNotification), {}, { kind: notification.kind }), dismiss: () => { dispatch(notifications.removeNotification(notification.id)); } }, notification.id); } case constants.NOTIFICATION_KINDS_PAGE['api-error']: { notification.text; const errorNotification = _objectWithoutProperties(notification, _excluded4); return jsxRuntime.jsx(ApiErrorNotification, { notification: errorNotification, dismiss: () => { dispatch(notifications.removeNotification(notification.id)); } }, notification.id); } case constants.NOTIFICATION_KINDS_PAGE['unexpected-error']: { notification.text; const errorNotification = _objectWithoutProperties(notification, _excluded5); return jsxRuntime.jsx(UnexpectedErrorNotification, { notification: _objectSpread$1(_objectSpread$1({}, errorNotification), {}, { values: _valuesInstanceProperty__default["default"](notification) }), dismiss: () => { dispatch(notifications.removeNotification(notification.id)); } }, notification.id); } default: return null; } }) }); }; const NotificationsListSide = props => { const dispatch = reactRedux.useDispatch(); const mapCustomNotificationToComponent = useCustomNotificationComponent(); const notifications$1 = reactRedux.useSelector(selectSideNotifications); return jsxRuntime.jsx("div", { id: `notifications-${props.domain}`, css: getStyles(props), children: _mapInstanceProperty__default["default"](notifications$1).call(notifications$1, notification => { // 1. Check if there is a custom notification component first const CustomNotificationComponent = mapCustomNotificationToComponent(notification); if (CustomNotificationComponent) { return jsxRuntime.jsx(CustomNotificationComponent, { notification: notification, dismiss: () => { dispatch(notifications.removeNotification(notification.id)); } }, notification.id); } switch (notification.kind) { case constants.NOTIFICATION_KINDS_SIDE.error: case constants.NOTIFICATION_KINDS_SIDE.warning: case constants.NOTIFICATION_KINDS_SIDE.info: case constants.NOTIFICATION_KINDS_SIDE.success: return jsxRuntime.jsx(GenericNotification, { notification: notification, dismiss: () => { dispatch(notifications.removeNotification(notification.id)); } }, notification.id); default: return null; } }) }); }; const NotificationsList = props => { switch (props.domain) { case constants.NOTIFICATION_DOMAINS.GLOBAL: return jsxRuntime.jsx(NotificationsListGlobal, _objectSpread$1({}, props)); case constants.NOTIFICATION_DOMAINS.PAGE: return jsxRuntime.jsx(NotificationsListPage, _objectSpread$1({}, props)); case constants.NOTIFICATION_DOMAINS.SIDE: return jsxRuntime.jsx(NotificationsListSide, _objectSpread$1({}, props)); default: return null; } }; NotificationsList.displayName = 'NotificationsList'; const _excluded = ["domain", "kind"]; function ownKeys(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context = ownKeys(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context2 = ownKeys(Object(t))).call(_context2, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; } const Notifier = _ref => { let _ref$domain = _ref.domain, domain = _ref$domain === void 0 ? constants.NOTIFICATION_DOMAINS.SIDE : _ref$domain, _ref$kind = _ref.kind, kind = _ref$kind === void 0 ? constants.NOTIFICATION_KINDS_SIDE.success : _ref$kind, props = _objectWithoutProperties(_ref, _excluded); const showNotification = actionsGlobal.useShowNotification(); react.useEffect(() => { const notification = showNotification({ domain: domain, kind: kind, text: props.text }, isNumber__default["default"](props.dismissAfter) ? _objectSpread(_objectSpread({}, props.meta), {}, { dismissAfter: props.dismissAfter }) : props.meta); return () => { // Remove notification when component "unmounts" notification.dismiss && notification.dismiss(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // We want to run this only once, when the component mounts the first time. Therefore we need to pass an empty array, even though the eslint rule shows a warning. return null; }; Notifier.propTypes = { domain: _pt__default["default"].any.isRequired, kind: _pt__default["default"].any.isRequired, text: _pt__default["default"].string, meta: _pt__default["default"].objectOf(_pt__default["default"].any), dismissAfter: _pt__default["default"].number }; Notifier.displayName = 'Notifier'; exports.ApiErrorMessage = ApiErrorMessage; exports.Notification = Notification; exports.NotificationProviderForCustomComponent = NotificationProviderForCustomComponent; exports.NotificationsList = NotificationsList; exports.Notifier = Notifier; exports.selectGlobalNotifications = selectGlobalNotifications; exports.selectPageNotifications = selectPageNotifications; exports.selectSideNotifications = selectSideNotifications; exports.version = version;